目录
一、概述
二、哈希碰撞
三、常见的哈希算法
四、简单应用代码实现
下面先以MD5算法对密码进行加密为例:
哈希算法的用途
扩展:
哈希算法(Hash)又称摘要算法(Digest ),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。哈希算法的目的:为了验证原始数据是否被篡改。
哈希算法最重要的特点就是:
·相同的输入一定得到相同的输出;
·不同的输入大概率得到不同的输出。
哈希碰撞是指,两个不同的输入得到了相同的输出,比如:
"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0
"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03
碰撞能不能避免?答案是不能。碰撞是一定会出现的,因为输出的气节长度是固定的,String的hashcode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
碰撞不可怕,我们担心的不是碰撞,而是碰撞的概率,因为碰撞概率的高低关系到哈希算法的安全性。一个安全的哈希算法必须满足:
·碰撞概率低;
·不能猜测输出。
算法 | 输出长度(位) | 输出长度(字节) |
MD5 | 128 bits | 16 bytes |
SHA-1 | 160bits | 20 bytes |
RipeMD-160 | 160bits | 20 bytes |
SHA-256 | 256 bits | 32 bytes |
SHA-512 | 512 bits | 64 bytes |
首先在使用MessageDigest时,我们首先根据哈希算法获取一个MessageDigest 实例;
然后,反复调用update(byte[])输入数据;
当输入结束后,调用digest()方法获得byte[]数组表示的摘要;
最后,把它转换为十六进制的字符串,借助String.format("%02x", bite)格式化控制。
public class demo02 {
public static void main(String[] args) {
try {
String password = "123wbjxxmynhmyzgq";
//根据当前算法,获取加密工具对象(摘要)
MessageDigest digest = MessageDigest.getInstance("MD5");
//更新原始数据
digest.update(password.getBytes());
//加密后的字节数组,转换字符串
byte[] resultByteArray = digest.digest();
StringBuilder result = new StringBuilder();
for(byte bite : resultByteArray) {
//字符串格式化控制:转换成16进制,每个数字占两个字节,不足0来补
result.append(String.format("%02x", bite));
}
System.out.println(result);
System.out.println(result.length());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
1.校验下载文件
我们只需要计算本地文件的哈希值,再与官网公开的哈希值对比,如果相同,说明文件下载正确,否则,说明文件已经被篡改。因为相同的输入永远会得到相同的输出,如果输入被修改了,得到的输出就会不同。
2.存储用户密码
为了防止密码泄露和抵御彩虹表的攻击,我们需要对每个口令额外添加随机数,这个方法称之为盐(salt)。
下面介绍用哈希算法SHA-1对密码进行加密:
①获取SHA-1算法的工具对象MessageDigest,通过getInstance("SHA-1")来进行创建;
②使用update()更新数据(添加数据);
③通过digest()方法获取加密信息(消息摘要);
④使用StringBuilder将加密信息格式化成16进制拼接成字符串输出。
public class demo04 {
public static void main(String[] args) throws NoSuchAlgorithmException {
String password = "wertyuisdf";
String salt = UUID.randomUUID().toString().substring(0, 5);
System.out.println(salt);
//获取SHA-1算法的工具对象
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(password.getBytes());
digest.update(salt.getBytes());
byte[] resultByteArray = digest.digest();
System.out.println(Arrays.toString(resultByteArray));
System.out.println(resultByteArray.length);
StringBuilder result = new StringBuilder();
for(byte b : resultByteArray) {
result.append(String.format("%02x", b));
}
System.out.println(result);
}
}
输出加密内容长度、加密内容:
首先介绍bouncyCastle:提供了很多哈希算法和加密算法的第三方开源库
下面介绍哈希算法 RipeMD-160算法实现过程:
①注册BouncyCastle提供的通知类对象BouncyCastleProvider;
②获取RipeMD160算法的“消息摘要对象”(加密对象)MessageDigest.getInstance("RipeMD160");
③更新原始数据update()
④通过调用digest()方法获取消息摘要(加密)
⑤输出具体信息,此次借助BigInteger(1,result).toString(16),1表示输出类型为正整数,16表示转换成16进制,将消息摘要输出。
public class demo {
public static void main(String[] args) {
try {
//注册BouncyCastle提供的通知类对象BouncyCastleProvider
Security.addProvider(new BouncyCastleProvider());
//获取RipeMD160算法的“消息摘要对象”(加密对象)
MessageDigest md = MessageDigest.getInstance("RipeMD160");
//更新原始数据
md.update("HelloWorld".getBytes());
//获取消息摘要(加密)
byte[] result = md.digest();
//消息摘要的字节长度和内容
System.out.println(result.length); //160位 = 20字节
System.out.println(Arrays.toString(result));
//16进制内容字符串
//1 代表类型为正整数
String hex = new BigInteger(1,result).toString(16);
System.out.println(hex.length()); //20字节 = 40个字符
System.out.println(hex);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
输出结果:
以上就是对于哈希算法总结以及简单应用实现的分享,如有不当之处还请大家多多评论指正,喜欢的话可以留下您的关注和点赞,一起学习,一起进步!