Вычислить хэш из потока неизвестной длины в С#
Какое лучшее решение в С# для вычисления "на лету" md5 подобно хешу потока неизвестной длины? В частности, я хочу вычислить хэш из данных, полученных по сети. Я знаю, что я получаю данные, когда отправитель завершает соединение, поэтому я заранее не знаю длины.
[EDIT] - Сейчас я использую md5, но для этого требуется второй проход по данным после его сохранения и записи на диск. Я бы предпочел использовать его на месте, поскольку он входит в сеть.
Ответы
Ответ 1
MD5, как и другие хэш-функции, не требует двух проходов.
Чтобы начать:
HashAlgorithm hasher = ..;
hasher.Initialize();
По мере поступления каждого блока данных:
byte[] buffer = ..;
int bytesReceived = ..;
hasher.TransformBlock(buffer, 0, bytesReceived, null, 0);
Чтобы закончить и получить хэш:
hasher.TransformFinalBlock(new byte[0], 0, 0);
byte[] hash = hasher.Hash;
Этот шаблон работает для любого типа, полученного из HashAlgorithm
, включая MD5CryptoServiceProvider
и SHA1Managed
.
HashAlgorithm
также определяет метод ComputeHash
, который принимает объект Stream
; однако этот метод блокирует поток до тех пор, пока поток не будет потреблен. Использование подхода TransformBlock
позволяет "асинхронный хэш", который вычисляется по мере поступления данных без использования потока.
Ответ 2
Класс System.Security.Cryptography.MD5 содержит метод ComputeHash, который принимает либо байт [], либо поток. Проверьте это на http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5_members.aspx
Ответ 3
В дополнение к ответу @peter-mourfield, вот код, который использует 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;
}
}
Поскольку и поток, и MD5 реализуют IDisposible, вам нужно использовать using(...){...}
Метод в примере кода возвращает ту же строку, которая используется для контрольной суммы MD5 в хранилище Azure Blob.
Ответ 4
Necromancing.
Две возможности в С#.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);
}
или с 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;
}