微信搜索:码农StayUp
主页地址:https://gozhuyinglong.github.io
源码分享:https://github.com/gozhuyinglong/blog-demos

1. 定义

单向散列函数(one-way hash function)是指对不同的输入值,通过单向散列函数进行计算,得到固定长度的输出值。这个输入值称为消息(message),输出值称为散列值(hash value)。

单向散列函数也被称为消息摘要函数(message digest function)、哈希函数或者杂凑函数。输入的消息也称为原像(pre-image)。输出的散列值也称为消息摘要(message digest)或者指纹(fingerprint),相当于该消息的身份证。

单向散列函数有多种实现算法,常见的有:MD5SHA-1SHA-2SHA-3

2. 特性

通过上面的定义,我们对单向散列函数的了解还是模糊的。下面介绍单向散列函数的特性,加深一下印象。

2.1 散列值长度固定

无论消息的长度有多少,使用同一算法计算出的散列值长度总是固定的。比如 MD5 算法,无论输入多少,产生的散列值长度总是 128 比特(16字节)。

然而比特是计算机能够识别的单位,而我们人类更习惯于使用十六进制字符串来表示(一个字节占用两位十六进制字符)。

2.2 消息不同其散列值也不同

使用相同的消息,产生的散列值一定相同。

使用不同的消息,产生的散列值也不相同。哪怕只有一个比特的差别,得到的散列值也会有很大区别。

这一特性也叫做抗碰撞性,对于抗碰撞性弱的算法,我们不应该使用。

2.3 具备单向性

只能通过消息计算出散列值,无法通过散列值反算出消息。

2.4 计算速度快

计算散列值的速度快。尽管消息越长,计算散列值的时间也越长,但也会在短时间内完成。

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。

算法名称 散列值长度 是否安全
MD5 128 不安全
SHA-1 160 不安全
SHA-224 224 安全
SHA-256 256 安全
SHA-384 384 安全
SHA-512 512 安全
SHA-512/224 224 安全
SHA-512/256 256 安全
SHA3-224 224 安全
SHA3-256 256 安全
SHA3-384 384 安全
SHA3-512 512 安全

4. 应用场景

单向散列函数并不能确保信息的机密性,它是一种保证信息完整性的密码技术。下面来看它的应用场景。

4.1 用户密码保护

用户在设置密码时,不记录密码本身,只记录密码的散列值,只有用户自己知道密码的明文。校验密码时,只要输入的密码正确,得到的散列值一定是一样的,表示校验正确。

为了防止彩虹表破解,还可以为密码进行加盐处理,只要验证密码时,使用相同的盐即可完成校验。

使用散列值存储密码的好处是:即使数据库被盗,也无法将密文反推出明文是什么,使密码保存更安全。

4.2 接口验签

为了保证接口的安全,可以采用签名的方式发送。

发送者与接收者要有一个共享秘钥。当发送者向接收者发送请求时,参数中附加上签名(签名由共享秘钥 + 业务参数,进行单向散列函数加密生成)。接收者收到后,使用相同的方式生成签名,再与收到的签名进行比对,如果一致,验签成功。

这样即可以验证业务参数是否被篡改,又能验明发送者的身份。

4.3 文件完整性校验

文件被挂载到网站时,同时也附上其散列值和算法,比如 Tomcat 官网。

用户下载后,计算其散列值,对比结果是否相同,从而校验文件的完整性。

4.4 云盘秒传

当我们将自己喜欢的视频放到网盘上时,发现只用了几秒的时间就上传成功了,而这个文件有几个G大小,是怎么做到的呢?

其实这个“秒传”功能可以利用单向散列函数来实现。

当我们上传一个文件时,云盘客户端会先为该文件生成一个散列值。拿着这个散列值去数据库中匹配,如果匹配到,说明该文件已经在云服务器存在。只需将该散列值与用户进行关联,便可完成本次“上传”。

这样,一个文件在云服务器上只会存一份,大大节约了云服务器的空间。

5. 代码实现

JDK的 java.security.MessageDigest 类为我们提供了消息摘要算法,用于 MD5和SHA的散列值生成。下面代码做了简单的封装,便于直接使用。

public class MDUtil {/*** MD5 加密** @param data 要加密的数据* @return 32位十六进制字符串*/public static String MD5(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("MD5");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** MD5 加密** @param data 要加密的数据* @return 32位十六进制字符串*/public static String MD5(String data) {return MD5(data.getBytes());}/*** SHA-1 加密** @param data 要加密的数据* @return 40位十六进制字符串*/public static String SHA1(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-1");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** SHA-1 加密** @param data 要加密的数据* @return 40位十六进制字符串*/public static String SHA1(String data) {return SHA1(data.getBytes());}/*** SHA-224 加密** @param data 要加密的数据* @return 56位十六进制字符串*/public static String SHA224(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-224");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** SHA-224 加密** @param data 要加密的数据* @return 56位十六进制字符串*/public static String SHA224(String data) {return SHA224(data.getBytes());}/*** SHA-256 加密** @param data 要加密的数据* @return 64位十六进制字符串*/public static String SHA256(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** SHA-256 加密** @param data 要加密的数据* @return 64位十六进制字符串*/public static String SHA256(String data) {return SHA256(data.getBytes());}/*** SHA-384 加密** @param data 要加密的数据* @return 96位十六进制字符串*/public static String SHA384(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-384");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** SHA-384 加密** @param data 要加密的数据* @return 96位十六进制字符串*/public static String SHA384(String data) {return SHA384(data.getBytes());}/*** SHA-512 加密** @param data 要加密的数据* @return 128位十六进制字符串*/public static String SHA512(byte[] data) {try {MessageDigest md = MessageDigest.getInstance("SHA-512");byte[] bytes = md.digest(data);return bytesToHexString(bytes);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}/*** SHA-512 加密** @param data 要加密的数据* @return 128位十六进制字符串*/public static String SHA512(String data) {return SHA512(data.getBytes());}/*** 将字节数组转换为十六进制字符串** @param bytes 字节数组* @return 十六进制字符串*/private static String bytesToHexString(byte[] bytes) {StringBuilder hexValue = new StringBuilder();for (byte b : bytes) {int val = b & 0xFF;if (val < 16) {hexValue.append("0");}hexValue.append(Integer.toHexString(val));}return hexValue.toString();}}

下面分别使用这些算法计算“123456”的散列值:

public static void main(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"));
}

输出结果:

MD5      e10adc3949ba59abbe56e057f20f883e
SHA-1    7c4a8d09ca3762af61e59520943dc26494f8941b
SHA-224  f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6
SHA-256  8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
SHA-384  0a989ebc4a77b56a6e2bb7b19d995d185ce44090c13e2984b7ecc6d446d4b61ea9991b76a4c2f04b1b4d244841449454
SHA-512  ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413

我用的是Java8,还不支持 SHA-3,所以上面代码只封装了MD5、SHA-1和SHA-2。

从Java9开始支持SHA-3

6. 完整代码

完整代码请访问我的Github,若对你有帮助,欢迎给个Star,谢谢!

https://github.com/gozhuyinglong/blog-demos/blob/main/java-source-analysis/src/main/java/io/github/gozhuyinglong/utils/MDUtil.java

7. 推荐阅读

  • 散列表(哈希表)

一文搞懂单向散列函数相关推荐

  1. 一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  2. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

  3. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  4. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

  5. 一文搞懂HMM(隐马尔可夫模型)-Viterbi algorithm

    ***一文搞懂HMM(隐马尔可夫模型)*** 简单来说,熵是表示物质系统状态的一种度量,用它老表征系统的无序程度.熵越大,系统越无序,意味着系统结构和运动的不确定和无规则:反之,,熵越小,系统越有序, ...

  6. 一文搞懂如何使用Node.js进行TCP网络通信

    摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...

  7. 【UE·蓝图底层篇】一文搞懂NativeClass、GeneratedClass、BlueprintClass、ParentClass

    本文将对蓝图类UBlueprint的几个UClass成员变量NativeClass.GeneratedClass.BlueprintClass.ParentClass进行比较深入的讲解,看完之后对蓝图 ...

  8. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下

    B站实操视频更新 跟着拉面学习AWS--EC2, IGW, RT, NAT, SG 简介 长文多图预警,看结论可以直接拖到"总结"部分 本文承接上一篇文章介绍以下 AWS 基础概念 ...

  9. 一文搞懂CAN FD总线协议帧格式

    目录 1.为什么会出现CAN FD? 2.什么是CAN FD? 3.CAN FD和CAN总线协议帧异同 4.解析CAN FD帧结构 4.1.帧起始 4.2.仲裁段 4.3.控制段 4.4.数据段 4. ...

最新文章

  1. 计算机数据恢复教程视频,视频删了怎么恢复?小技巧帮你快速解决
  2. 错误: java.lang.ClassNotFoundException: org.apache.commons.lang3.StringUtils
  3. centos7 下进行数据库自动备份
  4. int*类型和int类型_mysql8.0.19中在navicat客户端中int、bigint等类型设置长度保存后为0...
  5. python的进程线程和协程_python成长之路 :线程、进程和协程
  6. 设计模式-结构型模式-装饰模式
  7. OpenCV防止数据溢出saturate_cast
  8. Java默认值规则——各种数据类型的初试值是什么?
  9. 2017.8.10 树上染色 失败总结
  10. matlab矩阵代数计算,MATLAB学习笔记(五):符号计算(符号矩阵+代数方程组求解)...
  11. poj 1743 二分答案+后缀数组 求不重叠的最长重复子串
  12. js ul 清缓存_JavaScript使用ul中li标签实现删除效果
  13. python界面开发webview_Python+Appium学习篇之WebView处理
  14. python windows故障处理_python+windows automation windows有时会出现故障
  15. Spring Session + Redis 实现 Session 共享,附带 Nginx 集群
  16. 垃圾回收算法_划重点 JVM G1 垃圾回收算法
  17. SharePlex数据库复制解决方案
  18. 从PHP5到PHP7的注意事项
  19. Dw怎么背景图片设置
  20. python中分号中没内容_被“嫌弃”的分号的一生:不要在Python中使用无用分号了...

热门文章

  1. 【读书笔记】高效能人士的七个习惯
  2. DailyTask-关于电梯问题
  3. wps怎么将图片嵌入文字里_wps文档怎么把图片嵌入
  4. 嵌入式硬件工程师应具备哪些基本技能?
  5. EMV技术学习和研究(一)开篇
  6. Alfresco 4.0安装手把手教程
  7. 【Zeekr_Tech】整车OTA概述
  8. spark使用supervisor守护进程
  9. 光纤网卡千兆和万兆、光口和电口之间的区别?与PC网卡、HBA卡的区别有哪些?
  10. 郑豪7.16黄金高位盘旋不破,留意跌势悄然来袭,黄金晚间走势操作建议