"Заполнение недопустимо и не может быть удалено" с помощью 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);
}
}
Ответы
Ответ 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
.