ejemplo - Calcular un hash a partir de un flujo de longitud desconocida en C#
sha256 c# ejemplo (5)
¿Cuál es la mejor solución en C # para calcular un md5 "sobre la marcha" como un hash de una secuencia de longitud desconocida? Específicamente, quiero calcular un hash a partir de los datos recibidos a través de la red. Sé que he terminado de recibir datos cuando el remitente termina la conexión, por lo que no sé la longitud de antemano.
[EDITAR]: en este momento estoy usando md5, pero esto requiere una segunda pasada sobre los datos después de que se haya guardado y escrito en el disco. Prefiero atarlo en su lugar ya que viene de la red.
Además de la respuesta de @ peter-mourfield, aquí está el código que utiliza ComputeHash()
:
private static string CalculateMd5(string filePathName) {
using (var stream = File.OpenRead(filePathName))
using (var md5 = MD5.Create()) {
var hash = md5.ComputeHash(stream);
var base64String = Convert.ToBase64String(hash);
return base64String;
}
}
Dado que tanto el flujo como el implemento ID5 de MD5 son idóneos, debe usar el using(...){...}
El método en el ejemplo de código devuelve la misma cadena que se usa para la suma de comprobación MD5 en Azure Blob Storage.
Esto parece un caso de uso perfecto para CryptoStream
( docs ).
He usado CryptoStream
para procesar secuencias de longitud desconocida de resultados de bases de datos que deben ser comprimidos y luego transferidos a través de la red junto con un hash del archivo comprimido. Insertar un CryptoStream
entre el compresor y el escritor de archivos le permite calcular el hash sobre la marcha para que esté listo tan pronto como se escriba el archivo.
El enfoque básico se ve así:
var hasher = MD5.Create();
using (FileStream outFile = File.Create(filePath))
using (CryptoStream crypto = new CryptoStream(outFile, hasher, CryptoStreamMode.Write))
using (GZipStream compress = new GZipStream(crypto, CompressionMode.Compress))
using (StreamWriter writer = new StreamWriter(compress))
{
foreach (string line in GetLines())
writer.WriteLine(line);
}
// at this point the streams are closed so the hash is ready
string hash = BitConverter.ToString(hasher.Hash).Replace("-", "").ToLowerInvariant();
La clase System.Security.Cryptography.MD5 contiene un método ComputeHash que toma un byte [] o Stream. Compruébelo en http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5_members.aspx
MD5, al igual que otras funciones hash, no requiere dos pasadas.
Para comenzar:
HashAlgorithm hasher = ..;
hasher.Initialize();
A medida que llega cada bloque de datos:
byte[] buffer = ..;
int bytesReceived = ..;
hasher.TransformBlock(buffer, 0, bytesReceived, null, 0);
Para terminar y recuperar el hash:
hasher.TransformFinalBlock(new byte[0], 0, 0);
byte[] hash = hasher.Hash;
Este patrón funciona para cualquier tipo derivado de HashAlgorithm
, incluido MD5CryptoServiceProvider
y SHA1Managed
.
HashAlgorithm
también define un método ComputeHash
que toma un objeto Stream
; sin embargo, este método bloqueará el hilo hasta que se consuma la secuencia. El uso del enfoque TransformBlock
permite un "hash asíncrono" que se calcula a medida que los datos llegan sin utilizar un subproceso.
Nigromancia.
Dos posibilidades en C # .NET Core:
private static System.Security.Cryptography.HashAlgorithm GetHashAlgorithm(System.Security.Cryptography.HashAlgorithmName hashAlgorithmName)
{
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.MD5)
return (System.Security.Cryptography.HashAlgorithm) System.Security.Cryptography.MD5.Create();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA1)
return (System.Security.Cryptography.HashAlgorithm) System.Security.Cryptography.SHA1.Create();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA256)
return (System.Security.Cryptography.HashAlgorithm) System.Security.Cryptography.SHA256.Create();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA384)
return (System.Security.Cryptography.HashAlgorithm) System.Security.Cryptography.SHA384.Create();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA512)
return (System.Security.Cryptography.HashAlgorithm) System.Security.Cryptography.SHA512.Create();
throw new System.Security.Cryptography.CryptographicException($"Unknown hash algorithm /"{hashAlgorithmName.Name}/".");
}
protected override byte[] HashData(System.IO.Stream data,
System.Security.Cryptography.HashAlgorithmName hashAlgorithm)
{
using (System.Security.Cryptography.HashAlgorithm hashAlgorithm1 =
GetHashAlgorithm(hashAlgorithm))
return hashAlgorithm1.ComputeHash(data);
}
o con BouncyCastle:
private static Org.BouncyCastle.Crypto.IDigest GetBouncyAlgorithm(
System.Security.Cryptography.HashAlgorithmName hashAlgorithmName)
{
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.MD5)
return new Org.BouncyCastle.Crypto.Digests.MD5Digest();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA1)
return new Org.BouncyCastle.Crypto.Digests.Sha1Digest();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA256)
return new Org.BouncyCastle.Crypto.Digests.Sha256Digest();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA384)
return new Org.BouncyCastle.Crypto.Digests.Sha384Digest();
if (hashAlgorithmName == System.Security.Cryptography.HashAlgorithmName.SHA512)
return new Org.BouncyCastle.Crypto.Digests.Sha512Digest();
throw new System.Security.Cryptography.CryptographicException(
$"Unknown hash algorithm /"{hashAlgorithmName.Name}/"."
);
} // End Function GetBouncyAlgorithm
protected override byte[] HashData(System.IO.Stream data,
System.Security.Cryptography.HashAlgorithmName hashAlgorithm)
{
Org.BouncyCastle.Crypto.IDigest digest = GetBouncyAlgorithm(hashAlgorithm);
byte[] buffer = new byte[4096];
int cbSize;
while ((cbSize = data.Read(buffer, 0, buffer.Length)) > 0)
digest.BlockUpdate(buffer, 0, cbSize);
byte[] hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
return hash;
}