当 CryptoStream 关闭时崩溃 "Padding is invalid and cannot be removed"
Crash with "Padding is invalid and cannot be removed" when the CryptoStream closes
tl;dr 加密和解密工作正常且正确。我检查了一切。当我没有阅读 CryptoStream
的全部内容时,问题就出现了。我使用 RijndaelManaged
class 并且该应用程序适用于 Windows Mobile 6.
此应用程序适用于 XMLs,它必须对写入磁盘的所有内容进行加密。它得到XML(在内存中)后,直接将内容加密到一个文件中。稍后,应用程序必须将所有这些小 XML 组合成一个大的,但与此同时我们可能需要 XML 中的一些信息。为了优化内存使用,我没有将整个 XML 加载到内存中,我使用了一个 XmlReader
,它只从 CryptoStream
中读取它需要的内容。但这会因 "Padding is invalid and cannot be removed".
而崩溃
例如,这非常有效:
using (var fileStream = new FileStream(ResponsePath, FileMode.Open))
using (var crpytoStream = new CryptoStream(fileStream, key.CreateDecryptor(), CryptoStreamMode.Read))
using (var reader = StreamReader.Create(crpytoStream, settings))
{
reader.ReadToEnd();
}
这不是:
using (var fileStream = new FileStream(ResponsePath, FileMode.Open))
using (var crpytoStream = new CryptoStream(fileStream, key.CreateDecryptor(), CryptoStreamMode.Read))
using (var reader = StreamReader.Create(crpytoStream, settings))
{
var buffer = new char[1024];
reader.ReadBlock(buffer , 0, 1024);
}
我有一个解决方案,但它看起来像一个糟糕的 hack,而且它不适合巨大的 XMLs。如果您知道更清洁的解决方案,我将不胜感激。
编辑:
我测试了从流中读取不同数量的字节。
- 如果我从 0 开始读取 1、2、3、... 或 1024 个字节,当我关闭流时它会崩溃。
- 如果我从 0 开始读取 1025, 1026, 1027, ... 或 2048 字节,关闭流时它不会崩溃。
- 如果我从 0 开始读取 2049、2050、... 或 4096 字节,它会崩溃
当我关闭流时。
- 如果我从0开始读取4097字节,关闭流时不会崩溃
- 如果我从0开始读取8192字节,它会崩溃
当我关闭流时。
IV 在读取之前加载。
解决方案是创建另一个流以读到最后。从 XML 中获取我需要的所有数据后,我创建了一个 StreamReader
并读取了所有内容。如果 XML 很大,这显然很糟糕。
using (var workaroundStream = new StreamReader(crpytoStream))
{
var workaroundBuffer = new char[1024];
while (workaroundStream.ReadBlock(workaroundBuffer, 0, 1024) != 0)
{
}
}
可以从加密数据的中间读取和解密
- 从块边界开始读取,AES 为 16 字节。
- 停止阅读任何块 bounbdry。
- 读取块大小的倍数。
- 无填充解密。
备注:
- 如果模式是 CBC,先读取一个块并将其用作 IV。
- 如果读到最后用填充解密。
AES 是一种基于块的加密算法,它一次处理一个块。因此,只要起点位于加密期间使用的块边界上并且长度是块大小的倍数,从开头以外的地方开始解密就没有问题。
对于 CBC 模式,先前的块在加密之前与数据进行异或运算,并且在解密时必须再次与解密数据进行异或运算。参见 CBC mode。
tl;dr 加密和解密工作正常且正确。我检查了一切。当我没有阅读 CryptoStream
的全部内容时,问题就出现了。我使用 RijndaelManaged
class 并且该应用程序适用于 Windows Mobile 6.
此应用程序适用于 XMLs,它必须对写入磁盘的所有内容进行加密。它得到XML(在内存中)后,直接将内容加密到一个文件中。稍后,应用程序必须将所有这些小 XML 组合成一个大的,但与此同时我们可能需要 XML 中的一些信息。为了优化内存使用,我没有将整个 XML 加载到内存中,我使用了一个 XmlReader
,它只从 CryptoStream
中读取它需要的内容。但这会因 "Padding is invalid and cannot be removed".
例如,这非常有效:
using (var fileStream = new FileStream(ResponsePath, FileMode.Open))
using (var crpytoStream = new CryptoStream(fileStream, key.CreateDecryptor(), CryptoStreamMode.Read))
using (var reader = StreamReader.Create(crpytoStream, settings))
{
reader.ReadToEnd();
}
这不是:
using (var fileStream = new FileStream(ResponsePath, FileMode.Open))
using (var crpytoStream = new CryptoStream(fileStream, key.CreateDecryptor(), CryptoStreamMode.Read))
using (var reader = StreamReader.Create(crpytoStream, settings))
{
var buffer = new char[1024];
reader.ReadBlock(buffer , 0, 1024);
}
我有一个解决方案,但它看起来像一个糟糕的 hack,而且它不适合巨大的 XMLs。如果您知道更清洁的解决方案,我将不胜感激。
编辑: 我测试了从流中读取不同数量的字节。
- 如果我从 0 开始读取 1、2、3、... 或 1024 个字节,当我关闭流时它会崩溃。
- 如果我从 0 开始读取 1025, 1026, 1027, ... 或 2048 字节,关闭流时它不会崩溃。
- 如果我从 0 开始读取 2049、2050、... 或 4096 字节,它会崩溃 当我关闭流时。
- 如果我从0开始读取4097字节,关闭流时不会崩溃
- 如果我从0开始读取8192字节,它会崩溃 当我关闭流时。
IV 在读取之前加载。
解决方案是创建另一个流以读到最后。从 XML 中获取我需要的所有数据后,我创建了一个 StreamReader
并读取了所有内容。如果 XML 很大,这显然很糟糕。
using (var workaroundStream = new StreamReader(crpytoStream))
{
var workaroundBuffer = new char[1024];
while (workaroundStream.ReadBlock(workaroundBuffer, 0, 1024) != 0)
{
}
}
可以从加密数据的中间读取和解密
- 从块边界开始读取,AES 为 16 字节。
- 停止阅读任何块 bounbdry。
- 读取块大小的倍数。
- 无填充解密。
备注:
- 如果模式是 CBC,先读取一个块并将其用作 IV。
- 如果读到最后用填充解密。
AES 是一种基于块的加密算法,它一次处理一个块。因此,只要起点位于加密期间使用的块边界上并且长度是块大小的倍数,从开头以外的地方开始解密就没有问题。
对于 CBC 模式,先前的块在加密之前与数据进行异或运算,并且在解密时必须再次与解密数据进行异或运算。参见 CBC mode。