Associated GZipStream / DeflateStream and CryptoStream (AES) break when reading

I want to compress and then encrypt my data, and to improve the speed (without having to write to byte arrays and vice versa) I decided to combine the streams used for compression and encryption together.

It works fine when I write (compress and encrypt) data, but when I try to read data (decompress and decrypt), the read operation is interrupted - just the Read once call reads exactly 0 bytes, because the first Read always returns 0. The loop, like and in the code below, it almost works, except that at some point Read stops returning anything> 0, although there is still data to read.

Everything up to the last few bytes is decompressed and decrypted perfectly.

The number of bytes remaining when this occurs remains unchanged for the same plaintext; for example, it is always 9 bytes for a particular string, but always 1 byte for another.

Below is the corresponding encryption and decryption code; any ideas as to what could go wrong?

Encryption:

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

decryption:

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

Solution (modification for encryption):

 // 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(); 
+4
source share
2 answers

The problem is the following line:

 csEncrypt.FlushFinalBlock(); 

If you remove this, the code will work.

The reason is that when writing to DeflateStream not all data is written to the base stream. This only happens when you call Close() or Dispose() explicitly or implicitly, leaving the using block.

So in your code this happens:

  • You Write() all the data in DeflateStream , which in turn writes most of the data to the underlying CryptoStream .
  • You call csEncrypt.FlushFinalBlock() , which closes CryptoStream .
  • You leave the block using DeflateStream , which tries to write the rest of the data into the already closed CryptoStream .
  • You leave the block using CryptoStream , which is called by FlushFinalBlock() , if it has not already been called.

The correct sequence of events:

  • You Write() all the data in DeflateStream , which in turn writes most of the data to the underlying CryptoStream .
  • You leave the block using DeflateStream , which writes the rest of the data to the already closed CryptoStream .
  • You leave the block using CryptoStream , which calls FlushFinalBlock() .

Although I would expect the write to the private thread to complete with an exception. I'm not sure why this is not happening.

+3
source

I had a similar problem recently while reading from a remote stream.

For me, the solution was to change the line that reads the entire stream in one call during the loop:

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

Remote streams may not always return the requested amount of data, so try reading the stream while you are reading enough bytes.

0
source

All Articles