哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。

哈希算法最重要的特点就是:

  1. 相同的输入一定得到相同的输出
  2. 不同的输入大概率得到不同的输出

哈希算法的目的就是为了验证原始数据是否被篡改。
Java字符串,hashCode()就是一个哈希算法,它的输入是任意字符串,输出的是固定的4字节int整数

  1. "hello".hashCode();
  2. "Hello, java".hashCode();
  3. "hello, bob".hashCode();
  4. String.format("%x","hello".hashCode()); //5e918d2

两个相同的字符串永远会计算出相同的hashCode,否则基于hashCode定位的HashMap就无法正常工作。
这也是为什么当我们自定义一个class时,覆写equals()方法时,我们必须正确覆写hashCode()方法。

正确使用**Map**必须保证作为**key**的对象必须正确覆写**equals()**方法。 复习:

编写equals 和 hashCode

哈希碰撞

哈希碰撞是指,两个不同的输入得到 了相同的输出

哈希冲突——扬哥:https://segmentfault.com/a/1190000037691009

  1. "AaAaAa".hashCode();
  2. "BBAaBB".hashCode();

哈希碰撞不可避免,因为StringhashCode()输出的是4字节整数,最多只有image.png
种输出,但输入的数据长度是不固定的,有无数种输入。
所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
一个安全的哈希算法必须满足:

  • 碰撞概率低
  • 不能猜测输出

不能猜测输出是指,输入任意一个bit的变化会造成输出完全不同,这样就很难从输出反推输入,(只能依靠暴力穷举)。

常用的哈希算法

算法 输出长度(位) 输出长度(字节)
MD5 128bits 16bytes
SHA-1 160bits 20bytes
RipeMd-160 160bits 20bytes
SHA-256 256bits 32bytes
SHa-512 512bits 64bytes

根据碰撞概率,哈希算法的输出长度越长,就越难产生碰撞,也就越安全。
Java标准库提供了常用的哈希算法,并且有一套统一的接口。
我们以MD5算法为例

  1. public class Main {
  2. public static void main(String[] args) {
  3. //创建一个MessageDigest实例
  4. MessageDigest md = MessageDigest.getInstance("MD5");
  5. //反复调用upadte输入数据
  6. md.upadte("Hello".getBytes("UTF-8"));
  7. md.update("World".getBytes("UTF-8"));
  8. byte[] result = md.digest();
  9. System.out.println(new BigInteger(1,result).toString(16));
  10. //68e109f0f40ca72a15e05cc22786f8e6
  11. }
  12. }

使用MessageDigest时,我们首先根据哈希算法获取一个MessageDigest实例,然后,反复调用 update(byte[])方法输入数据。当输入结束后,调用digest()方法获得byte[]数组表示的摘要,最后,把它转换为十六进制的字符串。

哈希算法的用途

因为相同的输入永远会得到相同的输出,因此,如果输入被修改了,得到的输出就会不同。
我们在网站上下载软件的时候,经常看到下载页显示的哈希
MySQL Community Downloads
image.png
下载完成后使用md5sum path命令image.png

windows

Get-FileHash是powershell的一个cmdlet,它根据输入的文件名和给定的算法计算文件的哈希值(默认为sha256)。
使用格式如下: Get-FileHash 文件名 -Algorithm 算法名
支持的算法如下: MACTripleDES、MD5、RIPEMD160、SHA1、SHA256、SHA384、SHA512

linux

在linux下可以使用以下命令计算 md5sum
sha1sum
sha256sum
sha512sum
shasum
sha224sum

哈希算法的另一个重要用途是存储用户口令。如果将用户口令存放到数据库中,会产生极大的安全风险。

  • 数据库管理员能够看到用户明文口令;
  • 数据库数据一旦泄漏,黑客即可获取用户明文口令

在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。

这样一来,数据库管理员看不到用户的原始口令。即使数据库泄漏,黑客也无法拿到用户的原始口令。
想要拿到用户的原始口令,必须用暴力穷举的方法,一个个口令地试,直到某个口令计算的MD5恰好等于指定的值。
然鹅,道高一尺,魔高一丈。
使用哈希口令时,还要注意防止彩虹表攻击。
什么是彩虹表?
暴力穷举会消耗大量的算力和时间。但是,如果有一个预先计算好的常用口令和它们的MD5对照表

常用口令 MD5
hello123 f30aa7a662c728b7407c54ae6bfd27d1
5201314 723d505516e0c197e42a6be3c0af910e
12345678 25d55ad283aa400af464c76d713c07ad

这个表就是彩虹表,如果用户使用了常用口令,黑客从MD5一下就能反查到原始口令,这就是为什么不要使用常用密码,以及不要使用生日作为密码的原因。
然鹅,魔高一丈,道高一丈一尺..
即使用户使用了常用口令,我们也可以采取措施来抵御彩虹表攻击,方法是对每个口令额外添加随机数,这个方法称之为加盐(salt)

  1. digest = md5(salt+inputPassword);

加盐的目的在于使黑客的彩虹表失效,即使用户使用常用口令,也无法从MD5反推原始口令。
然鹅…

SHA-1

SHA-1也是一种哈希算法,它的输出是160bits,即20字节。SHA-1是由美国国家安全局开发的,SHA算法实际是一个系列,包括SHA-0(已废弃),SHA-1,SHA-256,SHA-512等。
在Java中使用SHA-1,和使用MD5完全一样,只需要把算法名称改为"SHA-1"

类似的,计算SHA-256,我们需要传入名称"SHA-256",SHA-512需要传入名称"SHA-512"。Java标准库支持的所有哈希算法可以在这里查到。 :::danger 注意:MD5因为输出长度较短,短时间内破解是可能的,目前已经不推荐使用。 ::: 小结:
哈希算法可用于验证数据完整性,具有防篡改检测的功能
常用的哈希算法有MD5、SHA-1等。
用哈希存储口令时要考虑彩虹表攻击。