前言
本文主要对单向加密算法做个概况性的介绍,然后给出简单的加密算法 Java 实现(即MD5、SHA系列、Hmac)。
一、介绍
哈希算法(Hash)又称摘要算法(Digest),它的作用是:对任意一组输入数据进行计算,得到一个固定长度的输出摘要。
哈希算法最重要的特点就是:
- 相同的输入一定得到相同的输出;
- 不同的输入大概率得到不同的输出。
所以,哈希算法的目的:为了验证原始数据是否被篡改。
Java字符串的hashCode()就是一个哈希算法,它的输入是任意字符串,输出是固定的4字节int整数。
哈希碰撞是指:两个不同的输入得到了相同的输出。
因为输出的字节长度是固定的,String的hashCode()输出是4字节整数,最多只有4294967296种输出,但输入的数据长度是不固定的,有无数种输入。所以,哈希算法是把一个无限的输入集合映射到一个有限的输出集合,必然会产生碰撞。
哈希算法,根据碰撞概率,哈希算法的输出长度越长,就越难产生碰撞,也就越安全。
常用的哈希算法有:
算法 | 输出长度(位) | 输出长度(字节) |
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 |
单向加密的常用算法
1. MD5
Message Digest algorithm 5,信息摘要算法,MD5
- 一般用于确保信息的传输完整一致性,校验传输的数据是否被修改,一旦原始信息被修改,生成的 MD5 值将会变得很不同
- 算法能将任意大小、格式的文字或文件进行加密从而产生 128 bit(16 字节)的散列值。如同人的指纹,不同文本的 MD5 值是不同的。
- 极端情况:就是不同的字符串的 MD5 值一样,这叫哈希碰撞。2009 年中科院就已经实现了相应的碰撞算法,不过 MD5 应用仍然很广泛
- 一般不可破解,除非使用穷举法,难度依旧很大
2.SHA 家族
- 是一个密码散列函数家族,是 FIPS 所认证的安全散列算法
- 和 MD5 类似,都是对文本进行散列,产生一定长度的散列值
3. HMAC
Hash Message Authentication Code,散列消息鉴别码
- 是一种通过特别计算方式之后产生的消息认证码(MAC),使用密码散列函数,同时结合一个加密密钥。它可以用来保证数据的完整性,同时可以用来作某个消息的身份验证。
二、使用步骤
1.MD5
public static void main(String[] args) throws NoSuchAlgorithmException {
// 即将加密数据
String data = "即将加密的数据";
// 创建基于MD5算法的消息摘要对象
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(data.getBytes());// 向对象中添加数据
byte[] digestByte = md5.digest();// 对数据加密,并获得加密后的字节数组
System.out.println(Arrays.toString(digestByte));
BigInteger digestBig = new BigInteger(1,digestByte);//将加密数据转换为正的16进制字符串
System.out.println(digestBig.toString(16));//按照16进制输出
}
2.SHA 家族
public static void main(String[] args) throws Exception {
String data = "即将加密的数据";
// 拿到一个SHA转换器
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
sha1.update(data.getBytes());
// byte[] digestByte = sha1.digest();// 对数据加密,并获得加密后的字节数组
// System.out.println(Arrays.toString(digestByte));
BigInteger encData = new BigInteger(1,sha1.digest());
System.out.println(encData);
//加密之后转换为32进制字符串
System.out.println("encData = "+ encData.toString(32));
}
3. HMAC
首次创建,利用KeyGenerator创建密钥
/**
* MAC算法可选以下多种算法
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
public static final String KEY_MAC = "HmacMD5";
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String data = "即将加密的数据";
// String key = "给定的密钥";
// 产生密钥
// 获取HmacMD5密钥生成器
KeyGenerator keyGen = KeyGenerator.getInstance(KEY_MAC);
// 生成密钥
SecretKey secretKey = keyGen.generateKey();
System.out.println(Arrays.toString(keyGen.generateKey().getEncoded()));
// 生成密钥字节数组:[-104, 24, 115, -66, 28, 2, -36, -13, 44, 34, 50, -31, -66, -26, -98, 118, 94, -83, 31, -124, 59, -57, 46, 31, -122, 80, 90, 127, -127, 119, -16, 48, -13, -70, -53, -24, 20, -111, 44, -42, 122, 45, 44, -26, -3, -127, 95, 119, -38, -40, 57, -70, -13, 38, 93, 86, -102, -83, 124, 63, 70, -117, 54, 60]
Mac mac = Mac.getInstance(KEY_MAC);
mac.init(secretKey);// 初始化密钥
mac.update(data.getBytes());
byte[] bytes = mac.doFinal();// 加密处理,并获取加密结果
BigInteger bigestencode = new BigInteger(1,bytes);
System.out.println(bigestencode.toString(16));
// 结果:7e78dd332d999e8a4345af95aea506af
}
后续密码对比
/**
* MAC算法可选以下多种算法
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
public static final String KEY_MAC = "HmacMD5";
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
String data = "即将加密的数据";
// 1.给定密钥的字节数组
byte[] keys = new byte[]{-104, 24, 115, -66, 28, 2, -36, -13, 44, 34, 50, -31, -66, -26, -98, 118, 94, -83, 31, -124, 59, -57, 46, 31, -122, 80, 90, 127, -127, 119, -16, 48, -13, -70, -53, -24, 20, -111, 44, -42, 122, 45, 44, -26, -3, -127, 95, 119, -38, -40, 57, -70, -13, 38, 93, 86, -102, -83, 124, 63, 70, -117, 54, 60};
// 2.给定密钥
String key = "给定的密钥";
// // 产生密钥
// // 获取HmacMD5密钥生成器
// KeyGenerator keyGen = KeyGenerator.getInstance(KEY_MAC);
// // 生成密钥
// SecretKey secretKey = keyGen.generateKey();
// 生成密钥
// 1.根据给定的密钥字节数组,第二参数指定一个密钥算法的名称
SecretKey secretKey1 = new SecretKeySpec(keys, KEY_MAC);
// 2.根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKey secretKey2 = new SecretKeySpec(key.getBytes(), KEY_MAC);
Mac mac = Mac.getInstance(KEY_MAC);
mac.init(secretKey1);// 初始化密钥
mac.update(data.getBytes());
byte[] bytes = mac.doFinal();// 加密处理,并获取加密结果
BigInteger bigestencode = new BigInteger(1,bytes);
System.out.println(bigestencode.toString(16));
// 结果:7e78dd332d999e8a4345af95aea506af
}
4.RipeMD-160
public static void main(String[] args) throws NoSuchAlgorithmException {
// 注册BouncyCastleBouncycastleProvider通知类
// 将提供的消息摘要算法注册至Security
Security.addProvider(new BouncyCastleProvider());
// 获取RipeMD160算法的"消息摘要对象"(加密对象)
MessageDigest ripeMd160 = MessageDigest.getInstance("RipeMD160");
// 更新原始数据
ripeMd160.update( "wbjxxmy".getBytes());
// 获取消息摘要(加密)
byte[] result = ripeMd160.digest( );
// 消息摘要的字节长度和内容
System.out.println("加密结果(字节长度):"+result.length);// 160位=20字节
System.out.println("加密结果(字节内容):"+Arrays.toString(result));
// 16进制内容字符串
String hex = new BigInteger(1,result).toString(16);
System.out.println("加密结果(字符串长度);"+hex.length()); //20字节=40个字符
System.out.println("加密结果(字符串内容):" +hex);
}
总结
常用的单向加密算法介绍和简单应用就是这些,MD5和SHA系列比较简单,就算大量数据泄露也不会有影响,但是为了防止彩虹表攻击,所以有了Hmac的出现。
RipeMD-160,Java中并不能直接调用,所以需要导入Jar包,最后写RipeMD-160的原因是为了注册BouncyCastleBouncycastleProvider通知类,将提供的消息摘要算法注册至Security,以防止有其他的注册行为,而导致加密算法无法使用。