«Заполнение недопустимо и не может быть удалено» с помощью AesManaged - PullRequest
28 голосов
/ 03 марта 2009

Я пытаюсь настроить простое шифрование / дешифрование при работе с 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);
  }
}

Ответы [ 5 ]

49 голосов
/ 03 марта 2009

Хитрость заключается в использовании 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

23 голосов
/ 01 декабря 2010

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

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

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

1 голос
/ 03 мая 2013

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

1 голос
/ 03 марта 2009

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

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

0 голосов
/ 12 июня 2019

Для чего это стоит, я документирую, с чем столкнулся. Я пытался прочитать поток памяти шифратора до того, как 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...