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

#c# #.net #encryption #hash

#c# #.net #поток #криптография #хэш

Вопрос:

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

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

 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, который принимает алгоритм хэша. Хэш-поток будет обновлять значение хэша при каждом вызове write / read .

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

 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?

Редактировать:

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

 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. Я думаю, вам следует перестраховаться и позвонить hashStream.FlushFinalBlock() после cryptStream.FlushFinalBlock()

2. Я пробовал это, но это вызвало исключение. По-видимому, FlushFinalBlock также вызывается в базовых потоках. Даже если он объявлен как Stream . Система. Метод NotSupportedException Message=FlushFinalBlock() дважды вызывался в криптопотоке. Его можно вызвать только один раз.

3. Вам не нужно явно вызывать FlushFinalBlock . В документах говорится, что «Вызов Close метода вызовет FlushFinalBlock «. Итак, после того, как ваш using будет закрыт, cryptoHash.Hash будет иметь значение хэша.

Ответ №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.Блок преобразования. Это может быть обернуто в потоковую реализацию, так что вы могли бы иметь HashStream то, что вы предлагаете. Не забудьте вызвать TransformFinalBlock перед получением хэш-значения.

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

Комментарии:

1. Знаете ли вы хорошую реализацию sync / async Stream wrapper для вычисления хэша во время чтения или записи?