『技术群里聊些啥』.NET 如何计算文件 MD5 哈希
Posted dotNET跨平台
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了『技术群里聊些啥』.NET 如何计算文件 MD5 哈希相关的知识,希望对你有一定的参考价值。
前言
有网友在交流群中询问,文件 MD5 是全部读取到内存后计算出来的,还是拿到流就可以计算出来了:
原理上来说,MD5 需要对全部内容做运算,所以应该是获取所有内容后再计算的。
但是,如果全部读取到内存后再计算,又不太现实,比如读取一个 1T 大小的文件。
Talk Is Cheap. Show Me The Code.
让我们来看看 .NET 中具体是如何实现的。
分析代码
.NET 下计算哈希的方法是ComputeHash
:
public byte[] ComputeHash(Stream inputStream)
if (_disposed)
throw new ObjectDisposedException(null);
// Use ArrayPool.Shared instead of CryptoPool because the array is passed out.
byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
int bytesRead;
int clearLimit = 0;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
if (bytesRead > clearLimit)
clearLimit = bytesRead;
HashCore(buffer, 0, bytesRead);
CryptographicOperations.ZeroMemory(buffer.AsSpan(0, clearLimit));
ArrayPool<byte>.Shared.Return(buffer, clearArray: false);
return CaptureHashCodeAndReinitialize();
通过while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
可以判断出,确实是获取了所有内容。
但是是分段获取的,每次最多只读取 4096 字节(byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
)。
关键是,分段读取出的字节,会合并放到内存中去计算哈希吗?
HashCore
分段读取出的字节是交由HashCore
方法处理的。
它的具体实现代码在LiteHash
结构中:
public void Append(ReadOnlySpan<byte> data)
if (data.IsEmpty)
return;
Check(Interop.Crypto.EvpDigestUpdate(_ctx, data, data.Length));
而调用的 EvpDigestUpdate 是底层 API,看不到源码:
internal const string CryptoNative = "libSystem.Security.Cryptography.Native.OpenSsl";
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDigestUpdate")]
private static partial int EvpDigestUpdate(SafeEvpMdCtxHandle ctx, ref byte d, int cnt);
在 OpenSSL 官网上,找到了这个 API 的描述:
将
d
处的数据字节哈希到摘要上下文ctx
中。可以在同一ctx
上多次调用此函数,以对增加的数据进行哈希处理。
也就是说,计算文件哈希实际经过了多次处理。
那如何得到最后的哈希值呢?
CaptureHashCodeAndReinitialize
ComputeHash最后调用 CaptureHashCodeAndReinitialize 方法返回哈希值。
它的具体实现代码也在LiteHash
结构中,调用了EvpDigestFinalEx API:
public int Finalize(Span<byte> destination)
Debug.Assert(destination.Length >= _hashSizeInBytes);
uint length = (uint)destination.Length;
Check(Interop.Crypto.EvpDigestFinalEx(_ctx, ref MemoryMarshal.GetReference(destination), ref length));
Debug.Assert(length == _hashSizeInBytes);
return _hashSizeInBytes;
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDigestFinalEx")]
internal static partial int EvpDigestFinalEx(SafeEvpMdCtxHandle ctx, ref byte md, ref uint s);
从
ctx
中检索摘要值并将其放在md
中。如果s
参数不是 NULL,则写入的数据字节数(即摘要的长度)将写入s
处。
结论
通过以上分析,可以得出文件 MD5 哈希计算流程如下:
不过,群里又有网友说,不要用 MD5:
这又是为什么呢?我们下回分解!
添加微信号【MyIO666】,邀你加入技术交流群
以上是关于『技术群里聊些啥』.NET 如何计算文件 MD5 哈希的主要内容,如果未能解决你的问题,请参考以下文章
『技术群里聊些啥』HttpClient 如何判断是同一终结点