.NET 6 中哈希算法的简化用法

2025-05-29 0 57

.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 定义如下:

  1. namespaceSystem.Security.Cryptography{
  2. publicpartialclassHMACMD5{
  3. publicstaticbyte[]HashData(byte[]key,byte[]source);
  4. publicstaticbyte[]HashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source);
  5. publicstaticintHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination);
  6. publicstaticboolTryHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);
  7. }
  8. publicpartialclassHMACSHA1{
  9. publicstaticbyte[]HashData(byte[]key,byte[]source);
  10. publicstaticbyte[]HashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source);
  11. publicstaticintHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination);
  12. publicstaticboolTryHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);
  13. }
  14. publicpartialclassHMACSHA256{
  15. publicstaticbyte[]HashData(byte[]key,byte[]source);
  16. publicstaticbyte[]HashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source);
  17. publicstaticintHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination);
  18. publicstaticboolTryHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);
  19. }
  20. publicpartialclassHMACSHA384{
  21. publicstaticbyte[]HashData(byte[]key,byte[]source);
  22. publicstaticbyte[]HashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source);
  23. publicstaticintHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination);
  24. publicstaticboolTryHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);
  25. }
  26. publicpartialclassHMACSHA512{
  27. publicstaticbyte[]HashData(byte[]key,byte[]source);
  28. publicstaticbyte[]HashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source);
  29. publicstaticintHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination);
  30. publicstaticboolTryHashData(ReadOnlySpan<byte>key,ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);
  31. }
  32. }

Sample Before

在之前的版本中想要实现计算 HMAC 算法会比较复杂,之前实现了一个 HashHelper 来封装了常用的 Hash 算法和 HMAC 算法,HashHelper 部分代码如下,完整代码可以从 Github 获取:https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/HashHelper.cs

  1. ///<summary>
  2. ///获取哈希之后的字符串
  3. ///</summary>
  4. ///<paramname="type">哈希类型</param>
  5. ///<paramname="source">源</param>
  6. ///<paramname="key">key</param>
  7. ///<paramname="isLower">是否是小写</param>
  8. ///<returns>哈希算法处理之后的字符串</returns>
  9. publicstaticstringGetHashedString(HashTypetype,byte[]source,byte[]?key,boolisLower=false)
  10. {
  11. Guard.NotNull(source,nameof(source));
  12. if(source.Length==0)
  13. {
  14. returnstring.Empty;
  15. }
  16. varhashedBytes=GetHashedBytes(type,source,key);
  17. varsbText=newStringBuilder();
  18. if(isLower)
  19. {
  20. foreach(varbinhashedBytes)
  21. {
  22. sbText.Append(b.ToString("x2"));
  23. }
  24. }
  25. else
  26. {
  27. foreach(varbinhashedBytes)
  28. {
  29. sbText.Append(b.ToString("X2"));
  30. }
  31. }
  32. returnsbText.ToString();
  33. }
  34. ///<summary>
  35. ///计算字符串Hash值
  36. ///</summary>
  37. ///<paramname="type">hash类型</param>
  38. ///<paramname="str">要hash的字符串</param>
  39. ///<returns>hash过的字节数组</returns>
  40. publicstaticbyte[]GetHashedBytes(HashTypetype,stringstr)=>GetHashedBytes(type,str,Encoding.UTF8);
  41. ///<summary>
  42. ///计算字符串Hash值
  43. ///</summary>
  44. ///<paramname="type">hash类型</param>
  45. ///<paramname="str">要hash的字符串</param>
  46. ///<paramname="encoding">编码类型</param>
  47. ///<returns>hash过的字节数组</returns>
  48. publicstaticbyte[]GetHashedBytes(HashTypetype,stringstr,Encodingencoding)
  49. {
  50. Guard.NotNull(str,nameof(str));
  51. if(str==string.Empty)
  52. {
  53. returnArray.Empty<byte>();
  54. }
  55. varbytes=encoding.GetBytes(str);
  56. returnGetHashedBytes(type,bytes);
  57. }
  58. ///<summary>
  59. ///获取Hash后的字节数组
  60. ///</summary>
  61. ///<paramname="type">哈希类型</param>
  62. ///<paramname="bytes">原字节数组</param>
  63. ///<returns></returns>
  64. publicstaticbyte[]GetHashedBytes(HashTypetype,byte[]bytes)=>GetHashedBytes(type,bytes,null);
  65. ///<summary>
  66. ///获取Hash后的字节数组
  67. ///</summary>
  68. ///<paramname="type">哈希类型</param>
  69. ///<paramname="key">key</param>
  70. ///<paramname="bytes">原字节数组</param>
  71. ///<returns></returns>
  72. publicstaticbyte[]GetHashedBytes(HashTypetype,byte[]bytes,byte[]?key)
  73. {
  74. Guard.NotNull(bytes,nameof(bytes));
  75. if(bytes.Length==0)
  76. {
  77. returnbytes;
  78. }
  79. HashAlgorithmalgorithm=null!;
  80. try
  81. {
  82. if(key==null)
  83. {
  84. algorithm=typeswitch
  85. {
  86. HashType.SHA1=>newSHA1Managed(),
  87. HashType.SHA256=>newSHA256Managed(),
  88. HashType.SHA384=>newSHA384Managed(),
  89. HashType.SHA512=>newSHA512Managed(),
  90. _=>MD5.Create()
  91. };
  92. }
  93. else
  94. {
  95. algorithm=typeswitch
  96. {
  97. HashType.SHA1=>newHMACSHA1(key),
  98. HashType.SHA256=>newHMACSHA256(key),
  99. HashType.SHA384=>newHMACSHA384(key),
  100. HashType.SHA512=>newHMACSHA512(key),
  101. _=>newHMACMD5(key)
  102. };
  103. }
  104. returnalgorithm.ComputeHash(bytes);
  105. }
  106. finally
  107. {
  108. algorithm.Dispose();
  109. }
  110. }

使用示例如下:

  1. HashHelper.GetHashedBytes(HashType.MD5,"test");
  2. HashHelper.GetHashedBytes(HashType.MD5,"test".GetBytes());
  3. HashHelper.GetHashedBytes(HashType.MD5,"test","testKey");
  4. HashHelper.GetHashedBytes(HashType.MD5,"test".GetBytes(),"testKey".GetBytes());
  5. HashHelper.GetHashedString(HashType.MD5,"test");
  6. HashHelper.GetHashedString(HashType.SHA1,"test".GetBytes());
  7. HashHelper.GetHashedString(HashType.SHA256,"test","testKey");
  8. HashHelper.GetHashedString(HashType.MD5,"test".GetBytes(),"testKey".GetBytes());

New API Sample

有了新的 API 以后可以怎么简化呢,来看下面的示例:

  1. varbytes="test".GetBytes();
  2. varkeyBytes="test-key".GetBytes();
  3. //HMACMD5
  4. varhmd5V1=HMACMD5.HashData(keyBytes,bytes);
  5. varhmd5V2=HashHelper.GetHashedBytes(HashType.MD5,bytes,keyBytes);
  6. Console.WriteLine(hmd5V2.SequenceEqual(hmd5V1));
  7. //HMACSHA1
  8. varhsha1V1=HMACSHA1.HashData(keyBytes,bytes);
  9. varhsha1V2=HashHelper.GetHashedBytes(HashType.SHA1,bytes,keyBytes);
  10. Console.WriteLine(hsha1V2.SequenceEqual(hsha1V1));
  11. //HMACSHA256
  12. varhsha256V1=HMACSHA256.HashData(keyBytes,bytes);
  13. varhsha256V2=HashHelper.GetHashedBytes(HashType.SHA256,bytes,keyBytes);
  14. Console.WriteLine(hsha256V2.SequenceEqual(hsha256V1));
  15. //HMACSHA384
  16. varhsha384V1=HMACSHA384.HashData(keyBytes,bytes);
  17. varhsha384V2=HashHelper.GetHashedBytes(HashType.SHA384,bytes,keyBytes);
  18. Console.WriteLine(hsha384V2.SequenceEqual(hsha384V1));
  19. //HMACSHA512
  20. varhsha512V1=HMACSHA512.HashData(keyBytes,bytes);
  21. varhsha512V2=HashHelper.GetHashedBytes(HashType.SHA512,bytes,keyBytes);
  22. Console.WriteLine(hsha512V2.SequenceEqual(hsha512V1));

直接使用对应的 HMAC 哈希算法的 HashData 方法即可,传入对应的 key 和 原始内容就可以了,上面是和我们 HashHelper 封装的方法进行对比,看结果是否一致,都是一致的,输出结果如下:

.NET 6 中哈希算法的简化用法

More

对于普通的哈希算法,微软其实在 .NET 5 就已经支持了上面的用法,可以尝试一下下面的代码:

  1. varbytes="test".GetBytes();
  2. //MD5
  3. varmd5V1=MD5.HashData(bytes);
  4. varmd5V2=HashHelper.GetHashedBytes(HashType.MD5,bytes);
  5. Console.WriteLine(md5V2.SequenceEqual(md5V1));
  6. //SHA1
  7. varsha1V1=SHA1.HashData(bytes);
  8. varsha1V2=HashHelper.GetHashedBytes(HashType.SHA1,bytes);
  9. Console.WriteLine(sha1V2.SequenceEqual(sha1V1));
  10. //SHA256
  11. varsha256V1=SHA256.HashData(bytes);
  12. varsha256V2=HashHelper.GetHashedBytes(HashType.SHA256,bytes);
  13. Console.WriteLine(sha256V2.SequenceEqual(sha256V1));
  14. //SHA384
  15. varsha384V1=SHA384.HashData(bytes);
  16. varsha384V2=HashHelper.GetHashedBytes(HashType.SHA384,bytes);
  17. Console.WriteLine(sha384V2.SequenceEqual(sha384V1));
  18. //SHA512
  19. varsha512V1=SHA512.HashData(bytes);
  20. varsha512V2=HashHelper.GetHashedBytes(HashType.SHA512,bytes);
  21. 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

原文链接:https://mp.weixin.qq.com/s/rbSGnq83mmUT5sV_vuyrcg

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 .NET 6 中哈希算法的简化用法 https://www.kuaiidc.com/97623.html

相关文章

发表评论
暂无评论