¿Es posible calcular hash MD5 (u otro) con lecturas almacenadas?

Necesito calcular las sums de comprobación de archivos bastante grandes (gigabytes). Esto se puede lograr usando el siguiente método:

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; } 

Sin embargo, los archivos normalmente se escriben con anterioridad de forma amortiguada (por ejemplo, escribiendo 32 MB de una vez). Estoy tan convencido de que vi una anulación de una función hash que me permitió calcular un hash MD5 (u otro) al mismo tiempo que la escritura, es decir: calcular el hash de un buffer, y luego alimentar el hash resultante en la siguiente iteración .

Algo como esto: (pseudocódigo-ish)

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

hash ahora es similar a lo que se lograría ejecutando la función calcHash en todo el archivo.

Ahora, no puedo encontrar reemplazos como ese en .Net Framework 3.5, ¿estoy soñando? ¿Nunca existió, o soy pésimo en la búsqueda? El motivo por el que se realizan cálculos tanto de escritura como de sum de comprobación de una vez es porque tiene sentido debido a los archivos de gran tamaño.

Utiliza los métodos TransformBlock y TransformFinalBlock para procesar los datos en fragmentos.

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

Nota: Funciona (al menos con el proveedor MD5) enviar todos los bloques a TransformBlock y luego enviar un bloque vacío a TransformFinalBlock para finalizar el proceso.

Me gusta la respuesta anterior pero, para completar, y como solución más general, consulte la clase CryptoStream . Si ya está manejando flujos, es fácil ajustar su flujo en un CryptoStream , pasando HashAlgorithm como el parámetro 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)); 

Es posible que HashAlgorithm cerrar la secuencia antes de obtener el hash (para que HashAlgorithm sepa que ya está hecho).

Parece que puede usar TransformBlock / TransformFinalBlock , como se muestra en este ejemplo: Mostrar actualizaciones de progreso cuando se procesan archivos grandes

Se espera que los algoritmos Hash manejen esta situación y típicamente se implementan con 3 funciones:

hash_init() – Llamado para asignar recursos y comenzar el hash.
hash_update() – Llamado con nuevos datos a medida que llega.
hash_final() – Completa el cálculo y los recursos gratuitos.

Mire http://www.openssl.org/docs/crypto/md5.html o http://www.openssl.org/docs/crypto/sha.html para ejemplos buenos y estándar en C; Estoy seguro de que hay bibliotecas similares para su plataforma.

Simplemente tuve que hacer algo similar, pero quería leer el archivo de forma asincrónica. Está usando TransformBlock y TransformFinalBlock y me está dando respuestas consistentes con Azure, ¡así que creo que es correcto!

 private static async Task CalculateMD5Async(string fullFileName) { var block = ArrayPool.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.Shared.Return(block); } }