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

时间:2011-07-23 18:25:53

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

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

当我写入(压缩和加密)数据时,它工作得很好,但是当我尝试读取数据(解压缩和解密)时,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();

解密:

// 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();

2 个答案:

答案 0 :(得分:4)

问题在于以下几行:

csEncrypt.FlushFinalBlock();

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

原因是当您写入DeflateStream时,并非所有数据都写入基础流。仅当您通过离开Close()块明确或隐式地致电Dispose()using时,才会发生这种情况。

因此,在您的代码中,会发生这种情况:

  1. Write() DeflateStream CryptoStream的所有数据,然后将大部分数据写入基础csEncrypt.FlushFinalBlock()
  2. 致电CryptoStream,关闭using
  3. 您离开DeflateStream的{​​{1}}块,该块会尝试将其余数据写入已关闭的CryptoStream
  4. 您离开using的{​​{1}}块,如果尚未调用,则会调用CryptoStream
  5. 正确的事件顺序是:

    1. FlushFinalBlock() Write() DeflateStream的所有数据,然后将大部分数据写入基础CryptoStream
    2. 您离开using的{​​{1}}块,其中将其余数据写入已关闭的DeflateStream
    3. 您离开CryptoStream的{​​{1}}块,该using块会调用CryptoStream
    4. 虽然我希望写入封闭流会因异常而失败。我不确定为什么不发生这种情况。

答案 1 :(得分:0)

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

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

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

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