Вычислить хеш из потока неизвестной длины в C # - PullRequest
20 голосов
/ 01 сентября 2010

Какое лучшее решение в C # для вычисления md5 типа «на лету», такого как хэш потока неизвестной длины?В частности, я хочу вычислить хэш из данных, полученных по сети.Я знаю, что я получил данные, когда отправитель завершает соединение, поэтому я не знаю длины заранее.

[EDIT] - сейчас я использую md5, но для этого требуется второй проход черезданные после того, как они были сохранены и записаны на диск.Я бы предпочел хешировать его на месте, поскольку он поступает из сети.

Ответы [ 5 ]

52 голосов
/ 01 сентября 2010

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 допускает «асинхронный хеш», который вычисляется по мере поступления данных без использования потока.

11 голосов
/ 01 сентября 2010

Класс System.Security.Cryptography.MD5 содержит метод ComputeHash, который принимает либо byte [], либо Stream.Проверьте это в http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5_members.aspx

8 голосов
/ 27 апреля 2017

В дополнение к ответу @ 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 в хранилище BLOB-объектов Azure.

2 голосов
/ 16 февраля 2019

Это похоже на идеальный вариант использования для CryptoStream ( документы ).

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

Основной подход выглядит так:

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();
1 голос
/ 25 января 2018

Necromancing.

Два варианта в 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);
}

или с 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...