我对 .NETCryptoStream
类的一个怪癖感到困惑:它的Dispose()
方法读取密文末尾的内容,寻找它不应该的填充,CryprographicException
结果抛出 a。
下面的 C# 程序加密了几个字节,调整了密文数组的大小,以便在密文结束后有更多(无意义的)字节,然后尝试对其进行解密。重点是:
- 密文为 8 个字节,一个 3DES 密文块。由于我只将 6 个字节写入
CryptoStream
它并且它正在使用PaddingMode.PKCS7
(默认值),因此块中剩余的两个字节用填充值 0x02 填充。 - 密文数组随后被调整为 16 字节,两个 3DES 块。第二个块是未初始化的废话,不是有效的密码输出。
CryptoStream
解密时,我从;中读取了 6 个字节。我不是要求它解密成废话部分,我也不是依靠它识别填充来确定它何时到达明文的末尾。
问题是,当调用 decrypting 时(自动在块的末尾CryptoStream
),我得到一个带有消息“Bad Data”的消息。它的堆栈跟踪显示它正在执行,并且所有 16 个字节都已从 中消耗,而不仅仅是对应于实际加密数据的 8 个字节。Dispose()
using
CryptographicException
CryptoStream.FlushFinalBlock()
ciphertextStream
如果我删除调整ciphertext
数组大小的行,则程序可以正常工作。如果我tripleDes.Padding = PaddingMode.None
在解密之前这样做,程序也可以正常工作——但这基本上使填充字节成为明文的一部分,所以我宁愿不这样做。显然,问题与填充有关;据我所知,它已解密第二个块,并期望PKCS7
在其末尾找到有效的样式填充。
由于我只从 读取的内容足以CryptoStream
要求解密一个块,并且该块是正确填充的最终块,然后我关闭CryptoStream
而不读取任何内容,为什么流认为它需要读取另一个块和寻找更多的填充?为什么它甚至试图消耗更多的输入作为它的一部分Dispose()
?
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Test
{
class Program
{
static void Main(string[] args)
{
byte[] plaintext = { 0, 1, 2, 3, 4 };
using (SymmetricAlgorithm tripleDes = TripleDESCryptoServiceProvider.Create())
{
// Encrypt the plaintext
byte[] ciphertext;
using (MemoryStream ciphertextStream = new MemoryStream())
{
using (ICryptoTransform encryptor = tripleDes.CreateEncryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.WriteByte((byte)plaintext.Length);
cryptoStream.Write(plaintext, 0, plaintext.Length);
cryptoStream.FlushFinalBlock();
}
}
ciphertext = ciphertextStream.ToArray();
}
// *** Add some non-ciphertext garbage to the end ***
Array.Resize(ref ciphertext, ciphertext.Length + 8);
// Now decrypt it again
byte[] decryptedPlaintext;
using (MemoryStream ciphertextStream = new MemoryStream(ciphertext, false))
{
using (ICryptoTransform decryptor = tripleDes.CreateDecryptor())
{
using (CryptoStream cryptoStream = new CryptoStream(ciphertextStream, decryptor, CryptoStreamMode.Read))
{
int length = cryptoStream.ReadByte();
decryptedPlaintext = new byte[length];
int i = 0;
while (i < length)
{
int bytesRead = cryptoStream.Read(decryptedPlaintext, i, (length - i));
if (bytesRead == 0) break;
else i += bytesRead;
}
} // CryptographicException: "Bad Data"
}
}
System.Diagnostics.Debug.Assert(decryptedPlaintext.SequenceEqual(plaintext));
}
}
}
}