C# 从C中长度未知的流计算哈希#

C# 从C中长度未知的流计算哈希#,c#,hash,cryptography,stream,C#,Hash,Cryptography,Stream,对于计算长度未知的流的“动态”md5类散列,C#中的最佳解决方案是什么?具体地说,我想根据通过网络接收的数据计算一个散列。我知道当发送方终止连接时,我已经接收不到数据了,所以我事先不知道长度 [编辑]-现在我正在使用md5,并在数据保存并写入磁盘后对其进行第二次传递。我宁愿在它从网络进入时对其进行散列。System.Security.Cryptography.MD5类包含一个ComputeHash方法,该方法采用字节[]或流。签出。MD5与其他哈希函数一样,不需要两次传递 开始: HashAlg

对于计算长度未知的流的“动态”md5类散列,C#中的最佳解决方案是什么?具体地说,我想根据通过网络接收的数据计算一个散列。我知道当发送方终止连接时,我已经接收不到数据了,所以我事先不知道长度


[编辑]-现在我正在使用md5,并在数据保存并写入磁盘后对其进行第二次传递。我宁愿在它从网络进入时对其进行散列。

System.Security.Cryptography.MD5类包含一个
ComputeHash
方法,该方法采用
字节[]
。签出。

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
,该方法接受一个
对象;但是,此方法将阻塞线程,直到流被消耗。使用
TransformBlock
方法允许在数据到达时计算“异步哈希”,而不使用线程。

进一步了解@peter mourfield的答案,下面是使用
ComputeHash()的代码。

由于流和MD5都实现IDisposable,因此需要使用
using(…){…}

代码示例中的方法返回用于Azure Blob存储中MD5校验和的相同字符串。

Necromancing

C#.NET核心中有两种可能性:

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

这似乎是
加密流
()的完美用例

我使用了
CryptoStream
来处理未知长度的数据库结果流,这些数据流需要进行gzip压缩,然后与压缩文件的散列一起通过网络传输。在压缩器和文件编写器之间插入一个
加密流
,允许您动态计算散列,以便在写入文件后立即准备好散列

基本方法如下所示:

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();

我见过这些方法,但从未调查过它们的作用。我真丢脸。这看起来是可行的。它不适用于从网络接收流,然后用API CopyTo方法将流发送(复制)到文件系统的情况。CryptoStream解决了这个问题。谢谢分享,很有帮助!非常感谢,这确实适合“动态”散列。。。在阅读散列之前,没有手工制作的流分块到blocks感谢关于关闭流的评论。别担心,没有亡灵术。欢迎提供最新答案。
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();