Зашифрованный массив байтов Aes пуст

Я реализую методы шифрования/дешифрования, как в этом примере. Проблема в том, что массив байтов в методе EncryptString пуст; таким образом, результирующая строка тоже пуста. Как решить проблему?

using System.Security.Cryptography;
using System.Text;

Crypto crypto = new();

string test = "test";

string encrypted = crypto.EncryptString(test);
string decrypted = crypto.DecryptString(encrypted);

Console.WriteLine($"{test}\n{encrypted}\n{decrypted}");
Console.ReadKey();

class Crypto
{
    byte[] key = Encoding.UTF8.GetBytes("01234567890123456789012345678901");
    byte[] iv = new byte[16];

    public string EncryptString(string plainText)
    {
        using Aes aes = Aes.Create();
        aes.Key = key;
        aes.IV = iv;

        ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

        using MemoryStream memoryStream = new();
        using CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write);
        using StreamWriter streamWriter = new(cryptoStream);

        streamWriter.Write(plainText);

        // Array is empty here
        byte[] array = memoryStream.ToArray();

        return Encoding.UTF8.GetString(array);
    }

    public string DecryptString(string cipherText)
    {
        using Aes aes = Aes.Create();
        aes.Key = key;
        aes.IV = iv;

        ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

        using MemoryStream memoryStream = new(Encoding.UTF8.GetBytes(cipherText));
        using CryptoStream cryptoStream = new(memoryStream, decryptor, CryptoStreamMode.Read);
        using StreamReader streamReader = new(cryptoStream);

        string decrypted = streamReader.ReadToEnd();

        return decrypted;
    }
}

Есть причина, по которой образец на странице, с которой вы работаете, использует операторы using, а не объявления using. Возможно, начните именно с кода в образце, а затем изменяйте его более медленно/осторожно.

Damien_The_Unbeliever 11.04.2024 15:26

Да, похоже, что StreamWriter нужно удалить перед вызовом MemoryStream.

Perotto 11.04.2024 15:32

Вместо этого вы можете Flush()

Joel Coehoorn 11.04.2024 15:38

@JoelCoehoorn просто Flush() на StreamWriter в данном случае недостаточно. Да, он запишет данные в базовый CryptoStream и даже вызовет Flush по этому поводу CryptoStream, но вам нужно вызвать FlushFinalBlock, а этого не будет сделано. Однако закрытие StreamWriter в этом случае закроет базовый CryptoStream и очистит последний блок.

Evk 12.04.2024 11:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Обратите внимание, что в коде, который вы пытаетесь скопировать, есть:

using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
      //Write all data to the stream.
      swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();

StreamWriter закрывается (удаляется) перед вызовом msEncrypt.ToArray(). В вашем коде все по-другому - StreamWriter не закрывается до конца области видимости (из-за использования операторов), поэтому при использовании memoryStream.ToArray() в этот поток еще ничего не записывается. Простое исправление:

using MemoryStream memoryStream = new();
using CryptoStream cryptoStream = new(memoryStream, encryptor, CryptoStreamMode.Write);
using StreamWriter streamWriter = new(cryptoStream);

streamWriter.Write(plainText);
streamWriter.Close();
byte[] array = memoryStream.ToArray();

Или лучше — просто не используйте операторы using размером с областью действия там, где в этом нет необходимости.

Кроме того, НЕ используйте кодировку UTF8 для результата шифрования. Результатом шифрования являются двоичные данные, а не текст. Если вам необходимо сохранить его как строку, используйте что-то вроде кодировки base64, а не кодировку текста, например UTF8.

Хороший улов по части UTF8. Если вы не заметили, мне пришлось бы дать ответ (вики-сообщество, поскольку на самом деле это не касается вопроса), чтобы указать на это. Возможно, он также захочет пересмотреть кодировку раньше, поскольку строки .Net по умолчанию не имеют формата UTF8. Кроме того, я бы сделал так, чтобы тип Crypto требовал Key как часть конструктора, а не жестко его кодировал.

Joel Coehoorn 11.04.2024 15:42

@JoelCoehoorn iv в этом случае также жестко запрограммирован (все нули), что как бы противоречит его цели.

Evk 11.04.2024 15:46

Другие вопросы по теме