哈希算法总结(含哈希算法工具类的封装)

哈希算法

  • 哈希算法概述
  • 哈希算法的特点及目的
  • 什么是哈希碰撞
  • 常用的哈希算法
  • 三种常见哈希算法的封装(工具类)
  • 彩虹表攻击与"加盐"
  • 总结


哈希算法概述

哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
在我们之前的学习中,也接触到了一些哈希算法的使用,比如String字符串的hashash()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数。


哈希算法的特点及目的

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

  • 相同的输入一定得到相同的输出;
  • 不同的输入大概率得到不同的输出。

所以,哈希算法的目的:为了验证原始数据是否被篡改


什么是哈希碰撞

哈希碰撞是指:两个不同的输入得到了相同的输出。
例如我们熟知的通话重地,它们的内容完全不同,却得到了相同的哈希值:

"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03

这就是哈希碰撞的一个体现,并且哈希碰撞是不能避免的,因为输出的字节长度是有限的,而输入数据的内容是无限的。就好比String的hashCode()为例,它的输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入,所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
既然哈希碰撞无法避免,那么就应该去想办法降低碰撞的概率,碰撞概率的高低还会直接关系到哈希算法的安全性,那么一个安全的哈希算法就应该满足:

  1. 碰撞概率低
  2. 不能被反向猜测输出

那么什么叫做不能被反向猜测输出?就好比以下这个例子:

hashA("java001") = "123456";
hashA("java002") = "123457";
hashA("java003") = "123458";
....

不难猜出,hashA(“java004”)的值会是"123459",这样的哈希算法是不安全的,一个合格的安全的哈希算法是不能被轻易找到某种规律反推输入的。


常用的哈希算法

哈希算法,根据碰撞概率,哈希算法的输出长度越长,就越难产生碰撞,也就越安全

1.MD5算法,输出字节长度:16bytes;
2.SHA算法系列,SHA-1输出字节长度:20bytes,SHA-256输出字节长度:32bytes, SHA-512输出字节长度:64bytes;
3.第三方开源库的RipeMD160算法,输出字节长度:20bytes。


三种常见哈希算法的封装(工具类)

代码如下(示例):

//hash算法(消息摘要算法)工具类
public class HashTools {
	// 消息摘要对象
	private static MessageDigest digest;
	//私有化构造方法,该类不能被实例化,只可以调取静态方法
	private HashTools() {}
	// 通过消息摘要对象,处理加密内容
	private static String handler(String source) {
		digest.update(source.getBytes());//更新原始数据
		byte[] bytes = digest.digest();//获取信息摘要(加密)
		String hash = bytesToHex(bytes);//十六进制内容字符串
		return hash;
	}	
	// 按照MD5进行消息摘要计算(哈希计算)
	public static String digestByMD5(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("MD5");
		return handler(source);
	}
	// 按照RipeMD160进行消息摘要计算(哈希计算)
	public static String digestByRipeMD160(String source) throws NoSuchAlgorithmException {
		// 注册BouncyCastleProvider通知类
		// 将提供的消息摘要算法注册到Security
		Security.addProvider(new BouncyCastleProvider());
		digest = MessageDigest.getInstance("RipeMD160");
		return handler(source);
	}
	// 按照SHA-1进行消息摘要计算(哈希计算)
	public static String digestBySHA1(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("SHA-1");
		return handler(source);
	}
	// 按照SHA-256进行消息摘要计算(哈希计算)
	public static String digestBySHA256(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("SHA-256");
		return handler(source);
	}
	// 按照SHA-512进行消息摘要计算(哈希计算)
	public static String digestBySHA512(String source) throws NoSuchAlgorithmException {
		digest = MessageDigest.getInstance("SHA-512");
		return handler(source);
	}
	// 将字节数组转换成16进制字符串
	public static String bytesToHex(byte[] bytes) {
		StringBuilder ret = new StringBuilder();
		for (byte b : bytes) {
			// 将字节值转换成2为十六进制字符串
			ret.append(String.format("%02x", b));
		}
		return ret.toString();
	}
}

哈希算法总结(含哈希算法工具类的封装)_第1张图片


彩虹表攻击与"加盐"

什么是彩虹表?
因为哈希算法相同的输入一定得到相同的输出,所以在某种程度上,如果有一个预先计算好的常用口令和它们的MD5的对照表,这个表就是彩虹表。如果用户使用了常用口令,黑客从MD5一下就能反查到相似原始口令,然后再进行暴力枚举,就可以很轻松的反推出加密前的内容。
那么如何抵御彩虹表呢?可以对每个口令额外添加随机值,这种方法称之为:加盐(salt)

//通过随机加"盐",解决彩虹表问题
public class Salt{
	public static void main(String[] args) throws NoSuchAlgorithmException {
		//原始密码
		String passWord="daxigua2024";		
		//产生随机盐值
		String salt = UUID.randomUUID().toString().substring(0,4);		
		//创建基于SHA-1算法的消息摘要
		MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
		sha1.update(passWord.getBytes());
		sha1.update(salt.getBytes());
		//计算加密结果
		String digestHex = HashTools.bytesToHex(sha1.digest());
		System.out.println(digestHex);		
	}
}

哈希算法总结(含哈希算法工具类的封装)_第2张图片
哈希算法总结(含哈希算法工具类的封装)_第3张图片
哈希算法总结(含哈希算法工具类的封装)_第4张图片

注意:因为加盐是对每个加密的内容中添加额外的随机数,以确保加密内容的更加安全,这个随机性带来更强的安全形的同时也带来了加密内容的随机性,随机到的不同的盐值,加密后的结果天差地别,所以,牢记==“盐值”==是之后对该加密内容进行验证的关键所在!


总结

  • 哈希算法可用于验证数据完整性,具有防篡改检测的功能;
  • 常用的哈希算法有MD5、SHA系列、RipeMD160等;
  • 用哈希存储口令时要考虑彩虹表攻击。

你可能感兴趣的:(哈希算法,算法)