Подтвердить что ты не робот

"Заполнение недопустимо и не может быть удалено" с помощью AesManaged

Я пытаюсь получить простое шифрование/дешифрование, работающее с AesManaged, но я продолжаю получать исключение при попытке закрыть поток расшифровки. Строка здесь зашифровывается и дешифруется правильно, а затем я получаю CryptographicException "Заполнение недопустимо и не может быть удалено" после того, как Console.WriteLine выводит правильную строку.

Любые идеи?

MemoryStream ms = new MemoryStream();
byte[] rawPlaintext = Encoding.Unicode.GetBytes("This is annoying!");

using (Aes aes = new AesManaged())
{
  aes.Padding = PaddingMode.PKCS7;
  aes.Key = new byte[128/8];
  aes.IV = new byte[128/8];

  using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(),
                                            CryptoStreamMode.Write))
  {
    cs.Write(rawPlaintext, 0, rawPlaintext.Length);
    cs.FlushFinalBlock();
  }

  ms = new MemoryStream(ms.GetBuffer());
  using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(),
                                            CryptoStreamMode.Read))
  {
    byte[] rawData = new byte[rawPlaintext.Length];
    int len = cs.Read(rawData, 0, rawPlaintext.Length);
    string s = Encoding.Unicode.GetString(rawData);
    Console.WriteLine(s);
  }
}
4b9b3361

Ответ 1

Хитрость заключается в использовании MemoryStream.ToArray(). Я также изменил ваш код, чтобы он использовал CryptoStream для записи, как в шифровании, так и в расшифровке. И вам не нужно явно вызывать CryptoStream.FlushFinalBlock(), потому что вы имеете его в инструкции using(), и этот сброс произойдет на Dispose(). Для меня работает следующее.

byte[] rawPlaintext = System.Text.Encoding.Unicode.GetBytes("This is all clear now!");

using (Aes aes = new AesManaged())
{
    aes.Padding = PaddingMode.PKCS7;
    aes.KeySize = 128;          // in bits
    aes.Key = new byte[128/8];  // 16 bytes for 128 bit encryption
    aes.IV = new byte[128/8];   // AES needs a 16-byte IV
    // Should set Key and IV here.  Good approach: derive them from 
    // a password via Cryptography.Rfc2898DeriveBytes 
    byte[] cipherText= null;
    byte[] plainText= null;

    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
        {
            cs.Write(rawPlaintext, 0, rawPlaintext.Length);
        }

        cipherText= ms.ToArray();
    }


    using (MemoryStream ms = new MemoryStream())
    {
        using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
        {
            cs.Write(cipherText, 0, cipherText.Length);
        }

        plainText = ms.ToArray();
    }
    string s = System.Text.Encoding.Unicode.GetString(plainText);
    Console.WriteLine(s);
}

Кроме того, я думаю, вы знаете, что вам нужно явно установить Mode экземпляра AesManaged и использовать System.Security.Cryptography.Rfc2898DeriveBytes, чтобы получить ключ и IV из пароля и соли.

см. также:
- AesManaged

Ответ 2

Это исключение может быть вызвано несоответствием любого из нескольких параметров шифрования.

Я использовал Security.Cryptography.Debug интерфейс для отслеживания всех параметров, используемых в методах шифрования/дешифрования.

Наконец, я выяснил, что моя проблема заключалась в том, что я установил свойство KeySize после установки Key, в результате чего класс восстановил случайный ключ и не использовал ключ, который был изначально настроен.

Ответ 3

byte [] rawData = new байт [rawPlaintext.Length];

Вам нужно прочитать длину буфера, который, вероятно, включает в себя необходимое дополнение (IIRC, было несколько лет).

Ответ 4

Никто не ответил, что на самом деле MemoryStream.GetBuffer возвращает выделенный буфер, а не реальные данные в этом буфере. В этом случае он возвращает 256-байтовый буфер, в то время как он содержит только 32 байта зашифрованных данных.

Ответ 5

Для чего это стоит, я документирую, с чем я столкнулся. Я пытался прочитать поток памяти шифратора до того, как CryptoStream был закрыт. Я знаю, что это было наивно, и я потратил впустую день, отлаживая это.

    public static byte[] Encrypt(byte[] buffer, byte[] sessionKey, out byte[] iv)
    {
        byte[] encrypted;
        iv = null;
        using (AesCryptoServiceProvider aesAlg = new AesCryptoServiceProvider { Mode = CipherMode.CBC, Padding = PaddingMode.PKCS7 })
        {
            aesAlg.Key = sessionKey;
            iv = aesAlg.IV;
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(sessionKey, iv);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    csEncrypt.Write(buffer, 0, buffer.Length);

                    //This was not closing the cryptostream and only worked if I called FlushFinalBlock()
                    //encrypted = msEncrypt.ToArray(); 
                }

                encrypted = msEncrypt.ToArray();

                return encrypted;
            }
        }
    }

Перемещение потока памяти шифратора, считанного после закрытия потока, решило проблему. Как упоминал Чизо. Вам не нужно вызывать FlushFinalBlock() если вы используете блок using.