什么是哈希算法?
哈希算法(Hash)又称摘要算法(Digest),它是将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是哈希算法
,而通过原始数据映射之后得到的二进制值串就是哈希值(散列值)。
哈希碰撞
哈希是将不同的输入映射成独一无二的、固定长度的值。哈希碰撞是指两个不同的输入得到了同一个哈希值,就说明发生了"哈希碰撞"。
哈希碰撞产生原理
由于我们输出的字节长度是固定的,例如在String这个类中,它的hashcode()方法输出的是4字节整数,最多只有4294967296中输出,但输入的字节长度不是固定的,有无数种输入,也就是说我们要把一个无限的输入集合映射到一个有限的输出集合中,那么必然会产生碰撞。
"AaAaAa".hashCode(); // 0x7460e8c0
"BBAaBB".hashCode(); // 0x7460e8c0
"通话".hashCode(); // 0x11ff03
"重地".hashCode(); // 0x11ff03
哈希算法
我们常用碰撞概率的高低来衡量一个哈希算法的安全性。一个安全的哈希算法需要满足两个条件:
- 碰撞概率低
- 不能猜测输出
常用的哈希算法
算法
|
输出长度(位)
| 输出长度(字节) |
Jar包
|
MD5
|
128 bits
|
16 bytes
|
java.security.MessageDigest (Java标准类库)
|
SHA-1 |
160 bits
|
20 bytes
|
java.security.MessageDigest (Java标准类库)
|
RepeMD-160
|
160 bits
|
20 bytes
|
在BounceCastle第三方类库中,我们在使用的时候需要使用
security的addprovider方法将BounceCastle的通知类加进去入到BounceCastle中。
|
SHA-256
|
256 bits
|
32 bytes
|
java.security.MessageDigest (Java标准库中)
|
SHA-512
|
512 bits
|
64 bytes
|
java.security.MessageDigest (Java标准库中)
|
下来我们来简单介绍一下MD5算法和SHA-1算法
MD5
MD5算法指MD5信息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计。
下面是一个简单的使用场景:存储用户密码,对我们的准备存入数据库中的密码进行加密。
加密步骤
- 创建一个MessageDigest实例,使用单例模式创建MD5算法的对象
- 使用update()方法更新原始数据
- 使用digest()方法对密码进行加密
- 将加密后的内容拼接为16进制的字符串
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) {
String password = "abc123";
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
//更新原始数据
digest.update(password.getBytes());
//获取加密后的内容
byte[] resultByteArray = digest.digest();
//加密后的字节数组,转换为字符串
StringBuilder sb = new StringBuilder();
System.out.println(resultByteArray.length);
for (byte b : resultByteArray) {
sb.append(String.format("%02x", b));
}
//输出密文
System.out.println(sb);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这样一来我们的密码就被加密处理了,可以使用密文在数据库中进行存储,但只这样存储,也会遭到黑客手中彩虹表的入侵,于是我们将采用特殊措施来抵御彩虹表攻击:对每个口令额外添加随机数,这个方法我们称为加"盐":
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) {
String password = "abc123";
//加盐
String salt = "cc3d";
try {
//使用MD5算法
MessageDigest digest = MessageDigest.getInstance("MD5");
//更新原始数据(加盐操作)
digest.update((password+salt).getBytes());
//获取加密后的数据
byte[] md5byteArray = digest.digest();
StringBuilder md5Str = new StringBuilder();
for (byte b : md5byteArray) {
md5Str.append(String.format("%02x", b));
}
System.out.printf("MD5算法(%d):%s\n",md5Str.length(),md5Str);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
SHA-1
SHA-1是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
SHA-1算法的使用和MD5算法的使用方式类似,只需将MD5改为SHA-1即可,在SHA-1算法中仍可以使用加"盐"的措施,使密文更加安全。
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) {
String password = "abc123";
//加盐
String salt = "cc3d";
try {
//使用SHA-1算法
MessageDigest digest = MessageDigest.getInstance("SHA-1");
//加"盐"操作
digest.update((password+salt).getBytes());
//获取加密后的数据
byte[] sha1byteArray = digest.digest();
StringBuilder sha1Str = new StringBuilder();
for (byte b : sha1byteArray) {
sha1Str.append(String.format("%02x", b));
}
System.out.printf("SHA-1算法(%d):%s\n`",sha1Str.length(),sha1Str);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}