Можно ли рассчитать MD5 (или другой) хэш с буферизованным чтением? - PullRequest
33 голосов
/ 23 января 2010

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

    private byte[] calcHash(string file)
    {
        System.Security.Cryptography.HashAlgorithm ha = System.Security.Cryptography.MD5.Create();
        FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
        byte[] hash = ha.ComputeHash(fs);
        fs.Close();
        return hash;
    }

Тем не менее, файлы, как правило, записываются заранее в буферизованном виде (скажем, написание 32 МБ за раз). Я настолько убежден, что увидел переопределение хеш-функции, которое позволило мне вычислять хеш-код MD5 (или другой) одновременно с записью, то есть: вычислять хеш-код одного буфера, а затем передавать полученный хеш-код в следующую итерацию. .

Примерно так: (псевдокод-иш)

byte [] hash = new byte [] { 0,0,0,0,0,0,0,0 };
while(!eof)
{
   buffer = readFromSourceFile();
   writefile(buffer);
   hash = calchash(buffer, hash);
}

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

Теперь я не могу найти никаких переопределений, подобных этому, в .Net 3.5 Framework, я мечтаю? Этого никогда не было, или я просто отвратительный в поисках? Причина одновременного выполнения записи и вычисления контрольной суммы заключается в том, что это имеет смысл из-за больших файлов.

Ответы [ 5 ]

49 голосов
/ 15 февраля 2011

Мне нравится ответ выше, но для полноты и для более общего решения обратитесь к классу CryptoStream. Если вы уже обрабатываете потоки, легко обернуть ваш поток в CryptoStream, передав HashAlgorithm в качестве параметра ICryptoTransform.

var file = new FileStream("foo.txt", FileMode.Open, FileAccess.Write);
var md5 = MD5.Create();
var cs = new CryptoStream(file, md5, CryptoStreamMode.Write);
while (notDoneYet)
{
    buffer = Get32MB();
    cs.Write(buffer, 0, buffer.Length);
}
System.Console.WriteLine(BitConverter.ToString(md5.Hash));

Возможно, вам придется закрыть поток перед получением хэша (поэтому HashAlgorithm знает, что это сделано).

46 голосов
/ 23 января 2010

Вы используете методы TransformBlock и TransformFinalBlock для обработки данных в чанках.

// Init
MD5 md5 = MD5.Create();
int offset = 0;

// For each block:
offset += md5.TransformBlock(block, 0, block.Length, block, 0);

// For last block:
md5.TransformFinalBlock(block, 0, block.Length);

// Get the has code
byte[] hash = md5.Hash;

Примечание: Он работает (по крайней мере, с поставщиком MD5) для отправки всех блоков на TransformBlockа затем отправьте пустой блок на TransformFinalBlock, чтобы завершить процесс.

4 голосов
/ 23 января 2010

Кажется, что вы можете использовать TransformBlock / TransformFinalBlock, как показано в этом примере: Отображение обновлений прогресса при хешировании больших файлов

3 голосов
/ 08 августа 2017

Мне просто нужно было сделать нечто подобное, но я хотел прочитать файл асинхронно. Он использует TransformBlock и TransformFinalBlock и дает мне ответы в соответствии с Azure, поэтому я думаю, что это правильно!

private static async Task<string> CalculateMD5Async(string fullFileName)
{
  var block = ArrayPool<byte>.Shared.Rent(8192);
  try
  {
     using (var md5 = MD5.Create())
     {
         using (var stream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, true))
         {
            int length;
            while ((length = await stream.ReadAsync(block, 0, block.Length).ConfigureAwait(false)) > 0)
            {
               md5.TransformBlock(block, 0, length, null, 0);
            }
            md5.TransformFinalBlock(block, 0, 0);
         }
         var hash = md5.Hash;
         return Convert.ToBase64String(hash);
      }
   }
   finally
   {
      ArrayPool<byte>.Shared.Return(block);
   }
}
3 голосов
/ 23 января 2010

Хеш-алгоритмы, как ожидается, справятся с этой ситуацией и обычно реализуются с 3 функциями:

hash_init() - Вызывается для выделения ресурсов и начала хэша.
hash_update() - вызывается с новыми данными по мере их поступления.
hash_final() - Завершить расчет и освободить ресурсы.

Посмотрите на http://www.openssl.org/docs/crypto/md5.html или http://www.openssl.org/docs/crypto/sha.html для хороших, стандартных примеров в C; Я уверен, что есть подобные библиотеки для вашей платформы.

...