当 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。如果您知道更清洁的解决方案,我将不胜感激。

编辑: 我测试了从流中读取不同数量的字节。

IV 在读取之前加载。

解决方案是创建另一个流以读到最后。从 XML 中获取我需要的所有数据后,我创建了一个 StreamReader 并读取了所有内容。如果 XML 很大,这显然很糟糕。

using (var workaroundStream = new StreamReader(crpytoStream))
{
    var workaroundBuffer = new char[1024];
    while (workaroundStream.ReadBlock(workaroundBuffer, 0, 1024) != 0)
    {
    }
}

可以从加密数据的中间读取和解密

  1. 从块边界开始读取,AES 为 16 字节。
  2. 停止阅读任何块 bounbdry。
  3. 读取块大小的倍数。
  4. 无填充解密。

备注:

  • 如果模式是 CBC,先读取一个块并将其用作 IV。
  • 如果读到最后用填充解密。

AES 是一种基于块的加密算法,它一次处理一个块。因此,只要起点位于加密期间使用的块边界上并且长度是块大小的倍数,从开头以外的地方开始解密就没有问题。

对于 CBC 模式,先前的块在加密之前与数据进行异或运算,并且在解密时必须再次与解密数据进行异或运算。参见 CBC mode

参见PKCS#7 padding