当前位置: 首页 > 后端技术 > Java

一文看懂单向哈希加密:MD5、SHA-1、SHA-2、SHA-3

时间:2023-04-02 01:28:34 Java

微信搜索:码农StayUp首页地址:https://gozhuyinglong.github.io源码分享:https://github.com/gozhuyinglong/blog-demos1。单向哈希函数(one-wayhashfunction)的定义是指通过单向哈希函数计算不同的输入值,得到固定长度的输出值。此输入值称为消息,输出值称为哈希值。单向散列函数也称为消息摘要函数、散列函数或散列函数。输入消息也称为原像。输出的哈希值也称为消息摘要或指纹,相当于消息的身份证。单向哈希函数的实现算法有很多,常见的有:MD5、SHA-1、SHA-2、SHA-3。2.特点通过上面的定义,我们对单向哈希函数的理解还比较模糊。下面介绍单向哈希函数的特点,加深印象。2.1哈希值的长度是固定的无论消息的长度如何,使用相同的算法计算出的哈希值的长度总是固定的。比如MD5算法,不管输入多少,生成的哈希值的长度始终是128位(16字节)。但是,位是计算机可以识别的单位,而我们人类更习惯于用十六进制字符串来表示(一个字节占两个十六进制字符)。2.2不同的消息有不同的哈希值。如果使用相同的消息,则生成的哈希值必须相同。不同的消息会产生不同的哈希值。即使只有一位的差异,得到的哈希值也会有很大的不同。这个特性也叫抗碰撞,我们不应该把它用在抗碰撞能力弱的算法上。2.3具有单向性,只能通过消息计算哈希值,不能通过哈希值反向计算消息。2.4计算速度快哈希值的计算速度很快。虽然消息越长,计算hash的时间也越长,但很快就搞定了。3.常用算法MD5和SHA-1已被破解,不应用于新用途;SHA-2和SHA-3仍然是安全的,可以使用。SHA-2包括:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。SHA-3包括:SHA3-224、SHA3-256、SHA3-384、SHA3-512。算法名称哈希值长度safeMD5128unsafeSHA-1160unsafeSHA-224224safeSHA-256256safeSHA-384384safeSHA-512512safeSHA-512/224224safeSHA-512/256256safeSHA3-224224safeSHA3-256256securitySHA3-384384securitySHA3-512512security4.应用场景单向散列函数不保证信息的机密性,它是一种保证信息完整性的密码学技术。我们来看看它的应用场景。4.1用户密码保护用户设置密码时,不记录密码本身,只记录密码的哈希值,只有用户自己知道密码的明文。验证密码时,只要输入的密码正确,得到的哈希值一定相同,说明验证正确。为了防止彩虹表被破解,还可以在密码中加盐。只要对密码进行验证,使用相同的salt即可完成验证。使用散列值存储密码的好处是,即使数据库被盗,也无法将密文逆转为明文,使密码存储更加安全。4.2接口签名验证为了保证接口的安全,可以通过签名方式发送。发送者和接收者必须共享一个密钥。发送方向接收方发送请求时,在参数上附加签名(签名是通过单向哈希函数对共享密钥+业务参数进行加密生成的)。收件人收到后,以同样的方式生成签名,然后与收到的签名进行比对。如果一致,则签名验证成功。这样既可以验证业务参数是否被篡改,也可以验证发送方的身份。4.3文件完整性校验文件挂载到网站时,也会附带其哈希值和算法,如Tomcat官网。用户下载后计算其哈希值并比较结果是否相同,从而验证文件的完整性。4.4云盘即时传输当我们把喜欢的视频放到网盘上时,发现只用了几秒钟就上传成功了,而且文件有好几G。我们是怎么做的?实际上,这种“二次传输”的功能可以通过使用单向哈希函数来实现。当我们上传一个文件时,云盘客户端会先为该文件生成一个哈希值。取这个哈希值并在数据库中匹配它。如果匹配,则说明该文件已经存在于云服务器上。这种“上传”只需将哈希值与用户相关联即可完成。这样,一个文件只会在云服务器上保存一份,大大节省了云服务器的空间。5、代码实现了JDK的java.security.MessageDigest类,为我们提供了MD5和SHA哈希值生成的消息摘要算法。下面代码简单封装一下,直接使用。publicclassMDUtil{/***MD5加密**@paramdata待加密数据*@return32位十六进制字符串*/publicstaticStringMD5(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("MD5");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}返回””;}/***MD5加密**@paramdata待加密数据*@return32位十六进制字符串*/publicstaticStringMD5(Stringdata){returnMD5(data.getBytes());}/***SHA-1加密**@paramdata待加密数据*@return40位十六进制字符串*/publicstaticStringSHA1(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-1");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}返回””;}/***SHA-1加密**@paramdata待加密数据*@return40位十六进制字符串*/publicstaticStringSHA1(Stringdata){returnSHA1(data.getBytes());}/***SHA-224加密**@paramdata待加密数据*@return56位十六进制字符串*/publicstaticStringSHA224(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-224");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}返回””;}/***SHA-224加密**@paramdata待加密数据*@return56位十六进制字符串*/publicstaticStringSHA224(Stringdata){returnSHA224(data.getBytes());}/***SHA-256加密**@paramdata待加密数据*@return64位十六进制字符串*/publicstaticStringSHA256(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-256");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}返回””;}/***SHA-256加密**@paramdata待加密数据*@return64位十六进制字符串*/publicstaticStringSHA256(Stringdata){returnSHA256(data.getBytes());}/***SHA-384加密**@paramdata待加密数据*@return96位十六进制字符串*/publicstaticStringSHA384(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-384");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}返回””;}/***SHA-384加密**@paramdata待加密数据*@return96位十六进制字符串*/publicstaticStringSHA384(Stringdata){返回SHA384(data.getBytes());}/***SHA-512加密**@paramdata待加密数据*@return128位十六进制字符串*/publicstaticStringSHA512(byte[]data){try{MessageDigestmd=MessageDigest.getInstance("SHA-512");byte[]bytes=md.digest(数据);返回bytesToHexString(字节);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();返回””;/***SHA-512加密**@paramdata待加密数据*@return128位十六进制字符串*/publicstaticStringSHA512(Stringdata){returnSHA512(data.getBytes());}/***将字节数组转换为十六进制字符串**@parambytes字节数组*@return十六进制字符串*/privatestaticStringbytesToHexString(byte[]bytes){StringBuilderhexValue=newStringBuilder();for(byteb:bytes){intval=b&0xFF;如果(val<16){hexValue.append("0");}hexValue.append(Integer.toHexString(val));}returnhexValue.toString();}}下面的算法用于计算“123456”的哈希值:publicstaticvoidmain(String[]args){System.out.println("MD5\t\t"+MDUtil.MD5("123456"));System.out.println("SHA-1\t"+MDUtil.SHA1("123456"));System.out.println("SHA-224\t"+MDUtil.SHA224("123456"));System.out.println("SHA-256\t"+MDUtil.SHA256("123456"));System.out.println("SHA-384\t"+MDUtil.SHA384("123456"));System.out.println("SHA-512\t"+MDUtil.SHA512("123456"));}输出结果:MD5e10adc3949ba59abbe56e057f20f883eSHA-17c4a8d09ca3762af61e59520943dc26494f8941bSHA-224f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6SHA-2568d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92SHA-3840a989ebc4a77b56a6e2bb7b19d995d185ce44090c13e2984b7ecc6d446d4b61ea9991b76a4c2f04b1b4d244841449454SHA-512ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413我用的是Java8,目前还不支持SHA-3,所以上面的代码只封装了MD5、SHA-1和SHA-2。从Java9开始,支持SHA-36。完整代码和完整代码请访问我的Github。有帮助,欢迎给个Star,谢谢!https://github.com/gozhuyinglong/blog-demos/blob/main/java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/MDUtil.java7。推荐阅读哈希表(hashtable)