Цепочки GZipStream / DeflateStream и CryptoStream (AES) разрывается при чтении - PullRequest
6 голосов
/ 23 июля 2011

Я хочу сжать и затем зашифровать свои данные, и для повышения скорости (без необходимости записи в байтовые массивы и обратно) решил объединить потоки, используемые для сжатия и шифрования.

Он отлично работает, когда я пишу (сжимаю и шифрую) данные, но когда я пытаюсь прочитать данные (распаковать и расшифровать), операция чтения прерывается - простой вызов 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 ]

4 голосов
/ 23 июля 2011

Проблема заключается в следующей строке:

csEncrypt.FlushFinalBlock();

Если вы удалите это, код будет работать.

Причина в том, что при записи в DeflateStream не все данные записываются в основной поток. Это происходит только тогда, когда вы вызываете Close() или Dispose() явно или неявно, оставляя блок using.

Итак, в вашем коде это происходит:

  1. Вы Write() все данные в DeflateStream, который в свою очередь записывает большинство данных в базовый CryptoStream.
  2. Вы звоните csEncrypt.FlushFinalBlock(), который закрывает CryptoStream.
  3. Вы покидаете блок using блока DeflateStream, который пытается записать оставшиеся данные в уже закрытый CryptoStream.
  4. Вы покидаете блок using CryptoStream, который вызовет FlushFinalBlock(), если он еще не был вызван.

Правильная последовательность событий:

  1. Вы Write() всех данных в DeflateStream, который в свою очередь записывает большинство данных в базовый CryptoStream.
  2. Вы покидаете блок using DeflateStream, который записывает оставшиеся данные в уже закрытый CryptoStream.
  3. Вы покидаете блок using CryptoStream, который вызывает FlushFinalBlock().

Хотя я ожидаю, что запись в закрытый поток не удастся, за исключением. Я не уверен, почему этого не происходит.

0 голосов
/ 23 июля 2011

У меня недавно была похожая проблема при чтении из удаленного потока.

Для меня решение было изменить строку, которая читает весь поток за один вызов, в , в то время как цикл :

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

Удаленные потоки не всегда могут вернуть запрошенный объем данных, поэтому попробуйте прочитать поток, пока вы читаете достаточно байтов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...