哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。哈希算法的目的:为了验证原始数据是否被篡改。
哈希算法最重要的特点就是:
●相同的输入一定得到相同的输出;
●不同的输入大概率得到不同的输出。
Java字符串的hashCode()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数:
"hello".hashCode(); // 0x5e918d2
"hello, java".hashCode(); // 0x7a9d88e8
"hello, bob".hashCode(); // 0xa0dbae2f
哈希碰撞
哈希碰撞是指,两个不同的输入得到了相同的输出:
"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0
"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03
哈希碰撞是我们不可避免的,因为输出的字节长度是固定的,String的hashCode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
常用哈希算法
因为我们需要控制加密后的长度一致,所以采用拼接字符串的方法,然后使用String.format("%02x",byte)方法与格式来控制每一位字节转为字符时都是两位,不足位用0补齐
常用的哈希算法有:根据碰撞概率,哈希算法的输出长度越长,就越难产生碰撞,也就越安全。
算法 | 输出长度(位) |
输出长度(字节) |
MD5 |
128 bits |
16 bytes |
SHA-1 |
160 bits |
20 bytes |
RipeMD-160 |
160 bits |
20 bytes |
SHA-256 |
256 bits |
32 bytes |
SHA-512 |
512 bits |
64 bytes |
MD5
Java标准库提供了常用的哈希算法,并且有一套统一的接口。我们以MD5算法为例,看看如何对输入计算哈希:
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) {
try {
// 获取基于MD5加密算法的工具对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 更新原始数据
md5.update("Hello".getBytes());
md5.update("World".getBytes());
// 加密后的结果
byte[] buff = md5.digest();
System.out.println(Arrays.toString(buff));
// 只要内容相同,加密的结果也相同
MessageDigest tempmd5 = MessageDigest.getInstance("MD5");
tempmd5.update("HelloWorld".getBytes());
buff = tempmd5.digest();
System.out.println(Arrays.toString(buff));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SHA-1
public class Test04 {
public static void main(String[] args) {
String password = "wbjxxmy";
// String salt = UUID.randomUUID().toString().substring(0,5);
String salt ="d4887";
System.out.println(salt);
// 获取SHA-1算法的工具对象
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
// 加"盐"(防止彩虹表攻击)
digest.update((password+salt).getBytes());
byte[] buff = digest.digest();
StringBuilder sb = new StringBuilder();
for(byte s : buff) {
// 设置String格式,以确保每位字节转为两位字符,不足两位用0补齐
sb.append(String.format("%02x", s));
}
System.out.println(sb);
System.out.println(sb.length());
System.out.println();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// MD5
try {
MessageDigest digestMD5 = MessageDigest.getInstance("MD5");
digestMD5.update((password+salt).getBytes());
byte[] buff = digestMD5.digest();
StringBuilder sb = new StringBuilder();
for(byte s : buff) {
sb.append