.NET 6 中哈希算法的简化用法
.NET 6 中哈希算法的簡化用法
Intro
微軟在 .NET 6 中引入一些更簡單的 API 來使用 HMAC 哈希算法(MD5/SHA1/SHA256/SHA384/SHA512)
微軟的叫法叫做 HMAC One-Shoot method, HMAC 算法在普通的哈希算法基礎上增加了一個 key,通過 key 提升了安全性,能夠有效避免密碼泄露被彩虹表反推出真實密碼, JWT(Json Web Token) 除了可以使用 RSA 方式外也支持使用 HMAC 。
New API
新增的 API 定義如下:
namespace?System.Security.Cryptography?{public?partial?class?HMACMD5?{public?static?byte[]?HashData(byte[]?key,?byte[]?source);public?static?byte[]?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source);public?static?int?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination);public?static?bool?TryHashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination,?out?int?bytesWritten);}public?partial?class?HMACSHA1?{public?static?byte[]?HashData(byte[]?key,?byte[]?source);public?static?byte[]?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source);public?static?int?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination);public?static?bool?TryHashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination,?out?int?bytesWritten);}public?partial?class?HMACSHA256?{public?static?byte[]?HashData(byte[]?key,?byte[]?source);public?static?byte[]?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source);public?static?int?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination);public?static?bool?TryHashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination,?out?int?bytesWritten);}public?partial?class?HMACSHA384?{public?static?byte[]?HashData(byte[]?key,?byte[]?source);public?static?byte[]?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source);public?static?int?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination);public?static?bool?TryHashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination,?out?int?bytesWritten);}public?partial?class?HMACSHA512?{public?static?byte[]?HashData(byte[]?key,?byte[]?source);public?static?byte[]?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source);public?static?int?HashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination);public?static?bool?TryHashData(ReadOnlySpan<byte>?key,?ReadOnlySpan<byte>?source,?Span<byte>?destination,?out?int?bytesWritten);} }Sample Before
在之前的版本中想要實現計算 HMAC 算法會比較復雜,之前實現了一個 HashHelper 來封裝了常用的 Hash 算法和 HMAC 算法,HashHelper 部分代碼如下,完整代碼可以從 Github 獲取:https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/HashHelper.cs
///?<summary> ///?獲取哈希之后的字符串 ///?</summary> ///?<param?name="type">哈希類型</param> ///?<param?name="source">源</param> ///?<param?name="key">key</param> ///?<param?name="isLower">是否是小寫</param> ///?<returns>哈希算法處理之后的字符串</returns> public?static?string?GetHashedString(HashType?type,?byte[]?source,?byte[]??key,?bool?isLower?=?false) {Guard.NotNull(source,?nameof(source));if?(source.Length?==?0){return?string.Empty;}var?hashedBytes?=?GetHashedBytes(type,?source,?key);var?sbText?=?new?StringBuilder();if?(isLower){foreach?(var?b?in?hashedBytes){sbText.Append(b.ToString("x2"));}}else{foreach?(var?b?in?hashedBytes){sbText.Append(b.ToString("X2"));}}return?sbText.ToString(); }///?<summary> ///?計算字符串Hash值 ///?</summary> ///?<param?name="type">hash類型</param> ///?<param?name="str">要hash的字符串</param> ///?<returns>hash過的字節數組</returns> public?static?byte[]?GetHashedBytes(HashType?type,?string?str)?=>?GetHashedBytes(type,?str,?Encoding.UTF8);///?<summary> ///?計算字符串Hash值 ///?</summary> ///?<param?name="type">hash類型</param> ///?<param?name="str">要hash的字符串</param> ///?<param?name="encoding">編碼類型</param> ///?<returns>hash過的字節數組</returns> public?static?byte[]?GetHashedBytes(HashType?type,?string?str,?Encoding?encoding) {Guard.NotNull(str,?nameof(str));if?(str?==?string.Empty){return?Array.Empty<byte>();}var?bytes?=?encoding.GetBytes(str);return?GetHashedBytes(type,?bytes); }///?<summary> ///?獲取Hash后的字節數組 ///?</summary> ///?<param?name="type">哈希類型</param> ///?<param?name="bytes">原字節數組</param> ///?<returns></returns> public?static?byte[]?GetHashedBytes(HashType?type,?byte[]?bytes)?=>?GetHashedBytes(type,?bytes,?null);///?<summary> ///?獲取Hash后的字節數組 ///?</summary> ///?<param?name="type">哈希類型</param> ///?<param?name="key">key</param> ///?<param?name="bytes">原字節數組</param> ///?<returns></returns> public?static?byte[]?GetHashedBytes(HashType?type,?byte[]?bytes,?byte[]??key) {Guard.NotNull(bytes,?nameof(bytes));if?(bytes.Length?==?0){return?bytes;}HashAlgorithm?algorithm?=?null!;try{if?(key?==?null){algorithm?=?type?switch{HashType.SHA1?=>?new?SHA1Managed(),HashType.SHA256?=>?new?SHA256Managed(),HashType.SHA384?=>?new?SHA384Managed(),HashType.SHA512?=>?new?SHA512Managed(),_?=>?MD5.Create()};}else{algorithm?=?type?switch{HashType.SHA1?=>?new?HMACSHA1(key),HashType.SHA256?=>?new?HMACSHA256(key),HashType.SHA384?=>?new?HMACSHA384(key),HashType.SHA512?=>?new?HMACSHA512(key),_?=>?new?HMACMD5(key)};}return?algorithm.ComputeHash(bytes);}finally{algorithm.Dispose();} }使用示例如下:
HashHelper.GetHashedBytes(HashType.MD5,?"test"); HashHelper.GetHashedBytes(HashType.MD5,?"test".GetBytes()); HashHelper.GetHashedBytes(HashType.MD5,?"test",?"testKey"); HashHelper.GetHashedBytes(HashType.MD5,?"test".GetBytes(),?"testKey".GetBytes());HashHelper.GetHashedString(HashType.MD5,?"test"); HashHelper.GetHashedString(HashType.SHA1,?"test".GetBytes()); HashHelper.GetHashedString(HashType.SHA256,?"test",?"testKey"); HashHelper.GetHashedString(HashType.MD5,?"test".GetBytes(),?"testKey".GetBytes());New API Sample
有了新的 API 以后可以怎么簡化呢,來看下面的示例:
var?bytes?=?"test".GetBytes(); var?keyBytes?=?"test-key".GetBytes();//?HMACMD5 var?hmd5V1?=?HMACMD5.HashData(keyBytes,?bytes); var?hmd5V2?=?HashHelper.GetHashedBytes(HashType.MD5,?bytes,?keyBytes); Console.WriteLine(hmd5V2.SequenceEqual(hmd5V1));//?HMACSHA1 var?hsha1V1?=?HMACSHA1.HashData(keyBytes,?bytes); var?hsha1V2?=?HashHelper.GetHashedBytes(HashType.SHA1,?bytes,?keyBytes); Console.WriteLine(hsha1V2.SequenceEqual(hsha1V1));//?HMACSHA256 var?hsha256V1?=?HMACSHA256.HashData(keyBytes,?bytes); var?hsha256V2?=?HashHelper.GetHashedBytes(HashType.SHA256,?bytes,?keyBytes); Console.WriteLine(hsha256V2.SequenceEqual(hsha256V1));//?HMACSHA384 var?hsha384V1?=?HMACSHA384.HashData(keyBytes?,bytes); var?hsha384V2?=?HashHelper.GetHashedBytes(HashType.SHA384,?bytes,?keyBytes); Console.WriteLine(hsha384V2.SequenceEqual(hsha384V1));//?HMACSHA512 var?hsha512V1?=?HMACSHA512.HashData(keyBytes?,bytes); var?hsha512V2?=?HashHelper.GetHashedBytes(HashType.SHA512,?bytes,?keyBytes); Console.WriteLine(hsha512V2.SequenceEqual(hsha512V1));直接使用對應的 HMAC 哈希算法的 HashData 方法即可,傳入對應的 key 和 原始內容就可以了,上面是和我們 HashHelper 封裝的方法進行對比,看結果是否一致,都是一致的,輸出結果如下:
More
對于普通的哈希算法,微軟其實在 .NET 5 就已經支持了上面的用法,可以嘗試一下下面的代碼:
var?bytes?=?"test".GetBytes();//?MD5 var?md5V1?=?MD5.HashData(bytes); var?md5V2?=?HashHelper.GetHashedBytes(HashType.MD5,?bytes); Console.WriteLine(md5V2.SequenceEqual(md5V1));//?SHA1 var?sha1V1?=?SHA1.HashData(bytes); var?sha1V2?=?HashHelper.GetHashedBytes(HashType.SHA1,?bytes); Console.WriteLine(sha1V2.SequenceEqual(sha1V1));//?SHA256 var?sha256V1?=?SHA256.HashData(bytes); var?sha256V2?=?HashHelper.GetHashedBytes(HashType.SHA256,?bytes); Console.WriteLine(sha256V2.SequenceEqual(sha256V1));//?SHA384 var?sha384V1?=?SHA384.HashData(bytes); var?sha384V2?=?HashHelper.GetHashedBytes(HashType.SHA384,?bytes); Console.WriteLine(sha384V2.SequenceEqual(sha384V1));//?SHA512 var?sha512V1?=?SHA512.HashData(bytes); var?sha512V2?=?HashHelper.GetHashedBytes(HashType.SHA512,?bytes); Console.WriteLine(sha512V2.SequenceEqual(sha512V1));很多時候我們可能都會要使用 MD5 或者 SHA1 之后的字符串,不知道為什么微軟沒有直接獲取一個字符串的方法,如果有這樣一個方法,就會更方便了,相比之后,感覺還是自己封裝的 HashHelper 使用起來更舒服一些,哈哈,這樣的靜態方法不夠抽象如果要動態替換哈希算法代碼可能就有點...
References
https://github.com/dotnet/runtime/pull/53487
https://github.com/dotnet/runtime/issues/40012
https://github.com/dotnet/core/issues/6569#issuecomment-913876347
https://baike.baidu.com/item/hmac/7307543?fr=aladdin
https://github.com/WeihanLi/SamplesInPractice/blob/master/net6sample/HashSample/Program.cs
https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/HashHelper.cs
總結
以上是生活随笔為你收集整理的.NET 6 中哈希算法的简化用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#开源类库推荐:拼多多开放平台SDK,
- 下一篇: 使用Pitcher简化卫语句