当前位置: 首页 > 科技观察

Hash算法在.NET6中的简化用法

时间:2023-03-13 11:38:52 科技观察

本文转载自微信公众号「amazingdotnet」作者WeihanLi。转载本文请联系amazingdotnet公众号。简介Microsoft在.NET6中引入了一些更简单的API以使用HMAC哈希算法(MD5/SHA1/SHA256/SHA384/SHA512)。微软的名字叫做HMACOne-Shootmethod,HMAC算法是在常见的hash算法的基础上加了一个key,通过key提高了安全性,可以有效防止密码泄露被逆向彩虹表推导出真正的密码。JWT(JsonWebToken)除了支持RSA方式外,还支持使用HMAC。新API新增的API定义如下:namespaceSystem.Security.Cryptography{publicpartialclassHMACMD5{publicstaticbyte[]HashData(byte[]key,byte[]source);publicstaticbyte[]HashData(ReadOnlySpankey,ReadOnlySpansource);publicstaticintHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination);publicstaticboolTryHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination,outintbytesWritten);}publicpartialclassHMACSHA1{publicstaticbyte[]HashData(byte[]key,byte[]source);publicstaticbyte[]HashData(ReadOnlySpankey,ReadOnlySpansource);publicstaticintHashData(ReadOnlySpankey,ReadOnlySpansource,Span目的地);publicstaticboolTryHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination,outintbytesWritten);}publicpartialclassHMACSHA256{publicstaticbyte[]HashData(byte[]key,byte[]sourcee);publicstaticbyte[]HashData(ReadOnlySpankey,ReadOnlySpansource);publicstaticintHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination);publicstaticboolTryHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination,outintbytesWritten);}publicpartialclassHMACSHA384{publicstaticbyte[]HashData(byte[]key,byte[]source);publicstaticbyte[]HashData(ReadOnlySpan键,ReadOnlySpan源);publicstaticintHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination);publicstaticboolTryHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination,outintbytesWritten);}publicpartialclassHMACSHA512{publicstaticbyte[]HashData(byte[]key,byte[]source);publicstaticbyte[]HashData(ReadOnlySpankey,ReadOnlySpansource);publicstaticintHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination);publicstaticboolTryHashData(ReadOnlySpankey,ReadOnlySpansource,Spandestination,outintbytesWritten);}}SampleBefore在之前的版本中计算HMAC算法会比较复杂。实现了一个HashHelper,封装了常用的Hash算法和HMAC算法。HashHelper的部分代码如下。完整代码可以从Github获取:https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/HashHelper.cs///

///获取字符串在散列之后//////ha希腊类型///source///key///是否为小写///哈希算法处理后的字符串publicstaticstringGetHashedString(HashTypetype,byte[]source,byte[]?key,boolisLower=false){Guard.NotNull(source,nameof(source));if(source.Length==0){returnstring.Empty;}varhashedBytes=GetHashedBytes(type,source,key);varsbText=newStringBuilder();if(isLower){foreach(varbinhashedBytes){sbText.Append(b.ToString("x2"));}}else{foreach(varbinhashedBytes){sbText.Append(b.ToString("X2")));}}returnsbText.ToString();}//////计算字符串哈希值//////哈希类型///待哈希的字符串///哈希字节数组publicstaticbyte[]GetHashedBytes(HashTypetype,stringstr)=>GetHashedBytes(type,str,Encoding.UTF8);//////计算字符串哈希值//////哈希类型///要计算的字符串哈希///编码类型///哈希字节数组publicstaticbyte[]GetHashedBytes(HashTypetype,stringstr,Encodingencoding){Guard.NotNull(str,nameof(str));if(str==string.Empty){returnArray.Empty();}varbytes=encoding.GetBytes(str);returnGetHashedBytes(type,bytes);}//////获取Hash后的字节数组//////hashType///原始字节数组///publicstaticbyte[]GetHashedBytes(HashTypetype,byte[]bytes)=>GetHashedBytes(type,bytes,null);//////获取Hash后的字节数组//////Hash类型///key///原始字节数组///publicstaticbyte[]GetHashedBytes(HashTypetype,byte[]bytes,byte[]?key){Guard.NotNull(bytes,nameof(bytes));if(bytes.Length==0){returnbytes;}HashAlgorithm=null!;try{if(key==null){algorithm=typeswitch{HashType.SHA1=>newSHA1Managed(),HashType.SHA256=>newSHA256Managed(),HashType.SHA384=>newSHA384Managed(),HashType.SHA512=>newSHA512Managed(),_=>MD5.Create()};}else{algorithm=typeswitch{HashType.SHA1=>newHMACSHA1(key),HashType.SHA256=>newHMACSHA256(key),HashType.SHA384=>newHMACSHA384(key),HashType.SHA512=>newHMACSHA512(key),_=>newHMACMD5(key)};}returnalgorithm.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());NewAPISample有了新API后如何简化,看以下示例:varbytes="test".GetBytes();varkeyBytes="test-key".GetBytes();//HMACMD5varhmd5V1=HMACMD5.HashData(keyBytes,bytes);varhmd5V2=HashHelper.GetHashedBytes(HashType.MD5,bytes,keyBytes);Console.WriteLine(hmd5V2.SequenceEqual(hmd5V1));//HMACSHA1varhsha1V1=HMACSHA1.HashData(keyBytes,bytes);varhsha1V2=HashHelper.GetHashedBytes(HashType.SHA1,bytes,keyBytes);Console.WriteLine(hsha1V2.SequenceEqual(hsha1V1));//HMACSHA256varhsha256V1=HMACSHA256.HashData(keyBytes,bytes);varhsha256V2=HashHelper.GetHashedBytes(HashType.SHA256,bytes,keyBytes);Console.WriteLine(hsha256V2.SequenceEqual(hsha256V1));//HMACSHA384VMAHBYTESHABYTESHA4.SHA384V1));varhsha384V2=HashHelper.GetHashedBytes(HashType.SHA384,bytes,keyBytes);Console.WriteLine(hsha384V2.SequenceEqual(hsha384V1));//HMACSHA512varhsha512V1=HMACSHA512.HashData(keyBytes,bytes);varhGetSha512V2=HashGetHashedBytes.SHA512,bytes,keyBytes);Console.WriteLine(hsha512V2.SequenceEqual(hsha512V1));直接使用对应的HMAC哈希算法的HashData方法,传入对应的key和原始内容即可。上面是和我们HashHelper封装的方法对比,看结果是否一致。他们都是一致的。输出结果如下:更多对于常见的hash算法,微软在.NET5中已经支持以上用法,可以试试下面的代码:varbytes="test".GetBytes();//MD5varmd5V1=MD5.HashData(bytes);varmd5V2=HashHelper.GetHashedBytes(HashType.MD5,bytes);Console.WriteLine(md5V2.SequenceEqual(md5V1));//SHA1varsha1V1=SHA1.HashData(bytes);varsha1V2=HashHelper.GetHashedBytes(HashType.SHA1,bytes));Console.WriteLine(sha1V2.SequenceEqual(sha1V1));//SHA256varsha256V1=SHA256.HashData(bytes);varsha256V2=HashHelper.GetHashedBytes(HashType.SHA256,bytes);Console.WriteLine(sha256V2.SequenceEqual(sha1V1));SHA3??84varsha384V1=SHA384.HashData(bytes);varsha384V2=HashHelper.GetHashedBytes(HashType.SHA384,bytes);Console.WriteLine(sha384V2.SequenceEqual(sha384V1));//SHA512varsha512V1=SHA512.HashGetPertesBytes.=varsha512V2(HashType.SHA512,bytes);控制台.WriteLine(sha512V2.SequenceEqual(sha512V1));很多时候我们可能想用MD5或者SHA1之后的字符串,不知道为什么微软没有直接获取字符串的方法。如果有这样的方法,就方便多了。和后者相比,感觉自己封装的HashHelper用起来更舒服,哈哈,像这样的静态方法不够抽象。如果要动态替换hash算法代码,可能有点...参考https://github.com/dotnet/runtime/pull/53487https://github.com/dotnet/runtime/issues/40012https://github.com/dotnet/core/issues/6569#issuecomment-913876347https://baike.baidu.com/item/hmac/7307543?fr=aladdinhttps://github.com/WeihanLi/SamplesInPractice/blob/master/net6sample/HashSample/Program.cshttps://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Helpers/HashHelper.cs