【问题标题】:Chained GZipStream/DeflateStream and CryptoStream (AES) breaks when reading读取时链接的 GZipStream/DeflateStream 和 CryptoStream (AES) 中断
【发布时间】:2011-07-23 18:25:52
【问题描述】:

我想压缩然后加密我的数据,为了提高速度(通过不必写入字节数组和返回)决定将用于压缩和加密的流链接在一起。

当我写入(压缩和加密)数据时它工作得很好,但是当我尝试读取数据(解压缩和解密)时,读取操作中断 - 只需调用 Read 一次读取正好 0 字节,因为第一次读取总是返回 0。在下面的代码中循环几乎可以工作,除了在某个点,Read 停止返回任何 > 0 的值,即使仍有数据要读取。

最后几个字节之前的所有内容都被完美地解压缩和解密。

发生这种情况时剩余的字节数对于相同的明文保持不变;例如,某个字符串总是 9 个字节,而另一个字符串总是 1 个字节。

以下是相关的加解密代码;关于可能出现什么问题的任何想法?

加密:

// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
    using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
    using (DeflateStream zip = new DeflateStream(csEncrypt, CompressionMode.Compress, true))
    {
        zip.Write(stringBytes, 0, stringBytes.Length);
        csEncrypt.FlushFinalBlock();

解密:

// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream())
{
    // Writes the actual data (sans prepended headers) to the stream
    msDecrypt.Write(stringBytes, prependLength, stringBytes.Length - prependLength);
    // Reset position to prepare for read
    msDecrypt.Position = 0;
    // init buffer to read to
    byte[] buffer = new byte[originalSize];

    using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
    using (DeflateStream zip = new DeflateStream(csDecrypt, CompressionMode.Decompress))
    {
        // Hangs with "offset" at a small, deterministic number away from originalSize (I've gotten 9 less and 1 less for different strings)
        // Loop fixed as per advice
        int offset = 0;
        while (offset < originalSize)
        {
            int read = zip.Read(buffer, offset, originalSize - offset);
            if (read > 0)
                offset += read;
            else if (read < 0)
                Console.WriteLine(read); // Catch it if it happens.
        }
        // Hangs with "left" at a small, deterministic number (I've gotten 9 and 1 for different strings)
        /*
        for (int left = buffer.Length; left > 0; )
            left -= zip.Read(buffer, 0, left);
        */

解决方案(修改加密):

// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
    using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV))
    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
    {
        using (DeflateStream zip = new DeflateStream(csEncrypt, CompressionMode.Compress, true))
            zip.Write(stringBytes, 0, stringBytes.Length);
        //Flush after DeflateStream is disposed.
        csEncrypt.FlushFinalBlock();

【问题讨论】:

  • 您每次都从字节 0 开始覆盖。您将需要添加一个偏移量变量,该变量从 0 开始并增加每次读取的字节数 - 否则您将继续覆盖缓冲区的开头。
  • 感谢您的关注;你就在那里 - 我用以下内容替换了循环:int offset = 0; while (offset &lt; originalSize) { offset += zip.Read(buffer, offset, originalSize - offset); } 不幸的是,这并没有解决问题;我仍然得到剩下的 1 个字节并且它挂起。 (回想起来,如果错误的循环是罪魁祸首,那么解密的数据就不会是完美的)。
  • 是的,当我看到它无法解决挂起字节的问题时,我将其作为答案删除,并将其添加为评论。我只是认为它可能会为您节省第二轮令人头疼的问题,想知道为什么缓冲区被破坏了:)
  • 这可能很奇怪...你可以尝试添加一个bytesRead 变量,它捕获读取的值,然后只运行逻辑等,如果这是 positive?我想知道它最后是否返回负数(iirc 这将是一个有效的终止符,因为从内存中它只是指出非正数是数据结束,尽管我最近没有检查过这方面的文档)
  • 试过了,它永远不会返回负数:

标签: c# .net encryption c#-4.0 compression


【解决方案1】:

问题出在下面一行:

csEncrypt.FlushFinalBlock();

如果您删除它,代码将起作用。

原因是当您写入DeflateStream 时,并非所有数据都写入底层流。仅当您通过离开 using 块显式或隐式调用 Close()Dispose() 时才会发生这种情况。

所以在您的代码中,会发生这种情况:

  1. 您将Write() 的所有数据写入DeflateStream,而后者又将大部分 数据写入底层CryptoStream
  2. 您调用csEncrypt.FlushFinalBlock(),这会关闭CryptoStream
  3. 您离开DeflateStreamusing 块,它会尝试将其余数据写入已经关闭的CryptoStream
  4. 您离开 CryptoStreamusing 块,如果尚未调用,它将调用 FlushFinalBlock()

正确的事件顺序是:

  1. 您将Write() 的所有数据写入DeflateStream,而后者又将大部分 数据写入底层CryptoStream
  2. 您离开DeflateStreamusing 块,它将其余数据写入已经关闭的CryptoStream
  3. 您离开了CryptoStreamusing 块,该块调用FlushFinalBlock()

虽然我希望写入关闭的流会失败并出现异常。我不确定为什么没有发生这种情况。

【讨论】:

  • 相反,该行似乎是写入所有基础数​​据所必需的。当我删除它时,加密数据更小,并且我在读取时收到 System.Security.Cryptography.CryptographicException,这无疑是因为加密数据无效。
  • @cervellous,对我来说就是这样。异常说明了什么?尝试的一种选择是在处置 DeflateStream 之后、处置 CryptoStream 之前调用 FlushFinalBlock()
  • 好吧,您的建议(在 deflatestream 被处理后移动刷新/数据处理部分)奏效了!不过不知道为什么。抱歉,我之前的评论不屑一顾。
【解决方案2】:

我最近在从远程流中读取时遇到了类似的问题。

对我来说,解决方案是将在单个调用中读取整个流的行更改为 while 循环:

zip.Write(stringBytes, 0, stringBytes.Length);

远程流并不总是可以返回请求的数据量,因此请在读取足够字节时尝试读取流。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-05
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    • 2012-05-18
    相关资源
    最近更新 更多