当前位置: 欣欣网 > 码农

.NET 9 Preview 1 支持 HashAlgorithm 的 one-shot Hash

2024-02-25码农

.NET 9 Preview 1 支持 算法 的 one-shot Hash

Intro

在之前的 .NET 版本里已经引入了一些 One-shot hash 的计算方式,如 SHA1.HashData / HMACSHA1.HashData 等方法

.NET 9 Preview 1 中引入了可以指定哈希算法的快速哈希方法,我们希望根据配置使用不同的哈希类型来获取哈希时可以很好的简化我们的代码

New API

新的 API 如下:

namespaceSystem.Security.Cryptography;
// Existing class
publicstaticpartial classCryptographicOperations {
publicstaticbyte[] HmacData(HashAlgorithmName hashAlgorithm, byte[] key, byte[] source);
publicstaticbyte[] HmacData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> key, ReadOnlySpan<byte> source);
publicstaticintHmacData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination);
publicstaticboolTryHmacData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> key, ReadOnlySpan<byte> source, Span<byte> destination, outint bytesWritten);
publicstaticbyte[] HmacData(HashAlgorithmName hashAlgorithm, byte[] key, Stream source);
publicstaticbyte[] HmacData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> key, Stream source);
publicstaticintHmacData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> key, Stream source, Span<byte> destination);
publicstatic ValueTask<byte[]> HmacDataAsync(HashAlgorithmName hashAlgorithm, byte[] key, Stream source, CancellationToken cancellationToken = default);
publicstatic ValueTask<intHmacDataAsync(HashAlgorithmName hashAlgorithm, ReadOnlyMemory<byte> key, Stream source, Memory<byte> destination, CancellationToken cancellationToken = default);
publicstatic ValueTask<byte[]> HmacDataAsync(HashAlgorithmName hashAlgorithm, ReadOnlyMemory<byte> key, Stream source, CancellationToken cancellationToken = default);
publicstaticbyte[] HashData(HashAlgorithmName hashAlgorithm, byte[] source);
publicstaticbyte[] HashData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> source);
publicstaticintHashData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> source, Span<byte> destination);
publicstaticboolTryHashData(HashAlgorithmName hashAlgorithm, ReadOnlySpan<byte> source, Span<byte> destination, outint bytesWritten);
publicstaticbyte[] HashData(HashAlgorithmName hashAlgorithm, Stream source);
publicstaticintHashData(HashAlgorithmName hashAlgorithm, Stream source, Span<byte> destination);
publicstatic ValueTask<intHashDataAsync(HashAlgorithmName hashAlgorithm, Stream source, Memory<byte> destination, CancellationToken cancellationToken = default);
publicstatic ValueTask<byte[]> HashDataAsync(HashAlgorithmName hashAlgorithm, Stream source, CancellationToken cancellationToken = default);
}

主要分成了两类,一类是 HashData ,直接计算哈希,一类是 HmacData Hmac 和直接的 Hash 相比,多了一个 key,更加具有抗碰撞性,TOTP 算法就是基于 HMAC 哈希算法来实现的

常用的 MD5/SHA1/SHA256 都是支持的,除此之外,还支持更安全的 SHA3 系列的 Hash 算法

Sample

可以对比一下新的哈希算法的 hash 方法和之前的 one-shot hash 的结果是否一致:

var helloBytes = "Hello World"u8;
var keyBytes = "keys"u8;
var sha1Bytes = SHA1.HashData(helloBytes);
var sha1HashedBytes = CryptographicOperations.HashData(HashAlgorithmName.SHA1, helloBytes);
Console.WriteLine(sha1HashedBytes.SequenceEqual(sha1Bytes));
var sha1MacBytes = HMACSHA1.HashData(keyBytes, helloBytes);
var sha1HmacBytes = CryptographicOperations.HmacData(HashAlgorithmName.SHA1, keyBytes, helloBytes);
Console.WriteLine(sha1HmacBytes.SequenceEqual(sha1MacBytes));

这里使用了 SHA1 / HMACSHA1 做了一个对比,比较两种方式的输出结果是否一致,输出结果均为 True

再来试一下其他的算法,使用示例如下:

var helloBytes = "Hello World"u8;
var keyBytes = "keys"u8;
PrintHashString(HashAlgorithmName.MD5, helloBytes);
PrintHashString(HashAlgorithmName.SHA1, helloBytes);
PrintHashString(HashAlgorithmName.SHA256, helloBytes);
PrintHashString(HashAlgorithmName.SHA384, helloBytes);
PrintHashString(HashAlgorithmName.SHA512, helloBytes);
// sha3
PrintHashString(HashAlgorithmName.SHA3_256, helloBytes);
PrintHashString(HashAlgorithmName.SHA3_384, helloBytes);
PrintHashString(HashAlgorithmName.SHA3_512, helloBytes);
PrintHmacString(HashAlgorithmName.MD5, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA1, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA256, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA384, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA512, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA3_256, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA3_384, keyBytes, helloBytes);
PrintHmacString(HashAlgorithmName.SHA3_512, keyBytes, helloBytes);
Console.WriteLine("Completed");

privatestaticvoidPrintHashString(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> bytes)
{
var hashedBytes = CryptographicOperations.HashData(hashAlgorithmName, bytes);
var hexString = Convert.ToHexString(hashedBytes);
Console.WriteLine($"{hashAlgorithmName}{hexString}");
}
privatestaticvoidPrintHmacString(HashAlgorithmName hashAlgorithmName, ReadOnlySpan<byte> key, ReadOnlySpan<byte> bytes)
{
var hashedBytes = CryptographicOperations.HmacData(hashAlgorithmName, key, bytes);
var hexString = Convert.ToHexString(hashedBytes);
Console.WriteLine($"{hashAlgorithmName}{hexString}");
}



输出结果如下:

output

More

前面的示例只演示了同步方法中 ReadOnlySpan 参数返回字节数组方法的使用,还支持异步方法以及从一个字节数组或者 Stream ,以及输出到一个 Span 中,并且支持异步方法,大家可以根据需要自己尝试一下,并根据进行选择使用

References

  • https://github.com/dotnet/runtime/issues/91407

  • https://github.com/dotnet/runtime/issues/62489

  • https://github.com/dotnet/runtime/issues/17590

  • https://github.com/dotnet/runtime/issues/40012

  • https://github.com/dotnet/runtime/pull/92430

  • https://cryptobook.nakov.com/cryptographic-hash-functions/secure-hash-algorithms

  • https://github.com/WeihanLi/SamplesInPractice/blob/main/net9sample/Net9Samples/HashSample.cs