I want to combine usage of CryptoStream and GZipStream, but only applied on certain bytes in a stream. I need to write to the stream both before and after applying crypto and compression. I can do one of them, but not both! And I just cannot spot the culprit.
Let me start with my test method only applying crypto - this works:
private static void WriteData(out byte[] data, out byte[] hash)
{
using (var memStream = new MemoryStream())
{
memStream.Write(new byte[] { 1 }, 0, 1);
using (var hashAlgorithm = SHA256.Create())
using (var cs = new CryptoStream(memStream, hashAlgorithm, CryptoStreamMode.Write, true))
{
cs.Write(new byte[] { 2 }, 0, 1);
cs.FlushFinalBlock();
hash = hashAlgorithm.Hash;
}
memStream.Write(new byte[] { 3 }, 0, 1);
data = memStream.ToArray();
}
}
private static void ReadData(byte[] data, out byte[] hash)
{
using (var memStream = new MemoryStream(data))
{
ReadByte(memStream, out byte value1);
using (var hashAlgorithm = SHA256.Create())
using (var cs = new CryptoStream(memStream, hashAlgorithm, CryptoStreamMode.Read, true))
{
ReadByte(cs, out byte value2);
cs.FlushFinalBlock();
hash = hashAlgorithm.Hash;
}
ReadByte(memStream, out byte value3);
}
}
private static void ReadByte(Stream stream, out byte value)
{
var bytes = new byte[1];
stream.Read(bytes, 0, 1);
value = bytes[0];
Console.WriteLine($"Read: {value}");
}
private static void ThisWorks()
{
WriteData(out byte[] data, out byte[] hash1);
ReadData(data, out byte[] hash2);
var match = hash1.SequenceEqual(hash2);
}
This is only using CryptoStream and I'm writing to the stream before and after. And this works as expected: Console writes 1,2,3 and the hashes match. However I cannot seem to combine GZipStream in this loop. Either hash does not match or the stream is at end after GZip (I cannot read the last byte 'value3'). This is what I would expect to work:
private static void WriteData_Both(out byte[] data, out byte[] hash)
{
using (var memStream = new MemoryStream())
{
memStream.Write(new byte[] { 1 }, 0, 1);
using (var hashAlgorithm = SHA256.Create())
using (var cs = new CryptoStream(memStream, hashAlgorithm, CryptoStreamMode.Write, true))
using (var gzip = new GZipStream(cs, CompressionMode.Compress, true))
{
gzip.Write(new byte[] { 2 }, 0, 1);
cs.FlushFinalBlock();
hash = hashAlgorithm.Hash;
}
memStream.Write(new byte[] { 3 }, 0, 1);
data = memStream.ToArray();
}
}
private static void ReadData_Both(byte[] data, out byte[] hash)
{
using (var memStream = new MemoryStream(data))
{
ReadByte(memStream, out byte value1);
using (var hashAlgorithm = SHA256.Create())
using (var cs = new CryptoStream(memStream, hashAlgorithm, CryptoStreamMode.Read, true))
using (var gzip = new GZipStream(cs, CompressionMode.Decompress, true))
{
ReadByte(gzip, out byte value2);
hash = hashAlgorithm.Hash;
}
ReadByte(memStream, out byte value3);
}
}
private static void ThisDoesNotWork()
{
WriteData_Both(out byte[] data, out byte[] hash1);
ReadData_Both(data, out byte[] hash2);
var match = hash1.SequenceEqual(hash2);
}
But it does not. After reading the byte (value2) from the gzip stream, the position in memStream jumps to the end. Which means that reading value3 fails. And the hash is also not correct. I have tried almost every combination of closing and flushing the various streams but I have to reach out for pointers at this stage.
Can anyone tell me how to insert GZipStream in the loop from the first working example?