我正在尝试实现内存中的AESManaged 加密/解密。这里的代码基于此:
加密/解密大文件 (.NET)
加密部分似乎可以工作,也就是说,没有例外。但是解密部分会抛出"索引超出数组范围"错误。
在前面的代码中,转换初始化如下:
aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(Key, salt, 1);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
transform = aes.CreateDecryptor(aes.Key, aes.IV);
void AESDecrypt(ref byte[] inB)
{
using (MemoryStream destination = new MemoryStream(inB, 0, inB.Length))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
{
try
{
using (MemoryStream source = new MemoryStream(inB, 0, inB.Length))
{
if (source.CanWrite==true)
{
source.Write(inB, 0, inB.Length);
source.Flush(); //<<inB is unchanged by the write
}
}
}
catch (CryptographicException exception)
{
if (exception.Message == "Padding is invalid and cannot be removed.")
throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
else
throw;
}
}
} <====At this point I get an IndexOutofBounds exception.
}
}
似乎违规的台词可能是: using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
你没有向CryptoStream提供任何数据,它需要一些数据,因为它试图删除填充。尝试注释掉源代码的整个 try/catch 块,您会收到相同的错误。
CryptoStream是空的,但你要求它读取填充。在"new AesManaged()"行之后,添加以下内容:aes.Padding = PaddingMode.None
。现在你的代码可以工作了,尽管它不会解密任何东西。由于您没有向CryptoStream提供任何内容,并且不要求它读取任何填充,因此它不再抱怨。它什么都不做。你有一个错误,你没有将密文提供给CryptoStream。
尝试这个代替源的内存流:
using (BinaryWriter source = new BinaryWriter(cryptoStream))
{
source.Write(inB, 0, inB.Length);
}
现在涉及CryptoStream,它将接收inB进行解密。
您可能在处理填充时遇到问题。当你的代码被编写(修复大括号拼写错误)时,你要求解密器去掉填充,但你没有修剪你的输出数组(ref byte[] inB),那么你怎么知道返回了多少数据?它将始终返回与输入相同的长度,但仅覆盖解密的数量。
下面是一些示例数据:
尝试使用 32 个零字节的键和 16 个零字节的 IV
:aes.Key = new byte[32];
aes.IV = new byte[16];
并将此密文解密为 inB:
byte[] inB = { 0xf9, 0x14, 0x32, 0x2a, 0x7a, 0x35, 0xf9, 0xef, 0x27, 0x98, 0x1a, 0x86, 0xe2, 0x80, 0x5e, 0x9b };
如果您没有设置 Padding.None,那么您会看到我的原始明文"Hello"仅覆盖 inB 的前五个字节。其余 11 个字节保持不变。填充已删除(默认值),并且未写入目标流。
现在设置 Padding.None 并尝试一下。由于我确实填充了数据,您将看到目标现在包含"Hello",后跟 11 个值为 11 的字节 - 填充。由于这次未删除填充,因此您会看到它已写入输出。
此外,正如 usr 所评论的那样,每次使用密钥加密时,IV 都应该是唯一的。您每次都派生相同的 IV 和密钥。如果此密钥只使用一次,那很好。如果多次使用同一密钥,则这是一个错误。IV应该是唯一的。它可以以明文形式发送 - 它不需要保密。