Вычислять хеш при записи в поток

В настоящее время я создаю зашифрованный формат файла, который должен быть подписан. Для этого мне нужно рассчитать хэш-код содержимого, которое я написал в поток.

В рамках .net существует множество алгоритмов хеширования, которые можно использовать, и он работает нормально, но для этого требуется, чтобы я обрабатывал поток три раза.

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

using (Stream fileStream = File.Open("myfile.bin", FileMode.Create))
{
    //Write content
    fileStream.Write(content, 0, content.Length);
}

byte[] hashValue = null;
using (Stream fileStream = File.Open("myfile.bin", FileMode.Open))
{
    HashAlgorithm hash = SHA256.Create();
    hashValue = hash.ComputeHash(fileStream);
}

using (Stream fileStream = File.Open("myfile.bin", FileMode.Append))
{
    fileStream.Write(hashValue, 0, hashValue.Length);
}

Это нормально, если он зашифрован в файл, но если он зашифрован для сетевого адресата, байты больше не доступны.

Поэтому в основном мне нужно обрабатывать данные только один раз. В CodeProject есть статья которая реализовала CRC32 как поток, который вычисляет код CRC32 каждый раз, когда к нему записываются данные.

Что-то вроде:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

using (FileStream fileStream = File.Create("myfile.bin"))
using (Stream crcStream = new CRCStream(fileStream)) //Takes a base stream
{
    //Write content
    crcStream.Write(content, 0, content.Length); 

    //Write checksum
    fileStream.Write(crcStream.WriteCRC, 0, 4);
}

Очевидно, что CRC32 не является агоритом хэша, но было бы неплохо иметь что-то вроде HashStream, которое принимает HashAlgorithm. HashStream будет обновлять значение хэша каждый раз, когда вызывается запись/чтение.

Что-то вроде:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

HashAlgorithm hashAlgo = SHA256.Create();

using (FileStream fileStream = File.Create("myfile.bin"))
using (HashStream hashStream = new HashStream(hashAlgo, fileStream))
{
    //Write content to HashStream 
    hashStream.Write(content, 0, content.Length);

    //Write checksum
    fileStream.Write(hashStream.HashValue, 0, hashAlgo.HashSize / 8);
}

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

Можно ли построить что-то вроде этого с использованием существующих компонентов .net framework?

Edit:

Спасибо, Питер! Я не знал, что CryptoStream может использовать HashAlgorithm. Поэтому для шифрования и хэширования в одно и то же время я мог бы сделать что-то вроде этого:

byte[] content = new byte[] { 0, 1, 2, 3, 4, 5, 6 };

SymmetricAlgorithm cryptoSymetric = Aes.Create();
HashAlgorithm cryptoHash = SHA256.Create();

using (FileStream file = new FileStream("Crypto.bin", FileMode.Create, FileAccess.Write))
using (CryptoStream hashStream = new CryptoStream(file, cryptoHash, CryptoStreamMode.Write))
using (CryptoStream cryptStream = new CryptoStream(hashStream, cryptoSymetric.CreateEncryptor(), CryptoStreamMode.Write))
{
    cryptStream.Write(content, 0, content.Length);
    cryptStream.FlushFinalBlock();

    byte[] hashValue = cryptoHash.Hash;

    file.Write(hashValue, 0, hashValue.Length);
}

Ответ 1

Это сделано для вас CryptoStream.

SHA256 hashAlg = new SHA256Managed();
CryptoStream cs = new CryptoStream(_out, hashAlg, CryptoStreamMode.Write);
// Write data here
cs.FlushFinalBlock();
byte[] hash = hashAlg.Hash;

Ответ 2

Вы можете выполнять поэтапные хэш-преобразования с помощью HashAlgorithm.TransformBlock. Это может быть завернуто в реализацию Stream, поэтому вы можете иметь HashStream, как вы предлагаете. Не забудьте вызвать TransformFinalBlock, прежде чем получить значение хэша.

HashAlgorithm является абстрактным, поэтому, конечно, вам нужно выбрать конкретную реализацию, которую вы хотите. Есть MD5, семья SHA и другие, чтобы выбрать из.