读取时链接的GZipStream/DeflateStream和CryptoStream(AES)会中断

cer*_*ous 6 .net c# compression encryption c#-4.0

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

它在我写入(压缩和加密)数据时工作得很好,但是当我尝试读取数据(解压缩和解密)时,Read操作中断 - 简单地调用Read一次读取0字节,因为第一个Read总是返回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();
Run Code Online (Sandbox Code Playgroud)

解密:

// 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);
        */
Run Code Online (Sandbox Code Playgroud)

解决方案(加密修改):

// 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();
Run Code Online (Sandbox Code Playgroud)

svi*_*ick 5

问题出在下面一行:

csEncrypt.FlushFinalBlock();
Run Code Online (Sandbox Code Playgroud)

如果删除它,代码就可以工作。

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

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

  1. 您将Write()所有数据写入DeflateStream,然后将大部分数据写入底层CryptoStream
  2. 你打电话csEncrypt.FlushFinalBlock(),这会关闭CryptoStream.
  3. 您离开using的块DeflateStream,它尝试将其余数据写入已经关闭的CryptoStream
  4. 您离开using的块CryptoStreamFlushFinalBlock()如果尚未调用它,它将调用 。

正确的事件顺序是:

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

尽管我预计写入关闭的流会因异常而失败。我不知道为什么这种情况没有发生。

  • 好吧,您的建议(在 deflatestream 处理后移动刷新/数据处理部分)有效!但不知道为什么。抱歉,我之前的评论不屑一顾。 (2认同)