消息摘要又称为散列算法,其核心是通过一个单向散列函数将原始数据映射到一组散列值, 即 f: 原始数据 -> 散列值;由于这个过程是单向的,即不能通过散列值反算出原始数据,所以它不能算是加密算法。消息摘要常用于数据的完整性验证。
本文将讨论几种常见的消息摘要算法。

MD5(第5代 Message Digest)

用途

MD5是常见的消息摘要算法。通常用于验证文件的完整性。另外一些用户的重要数据(如密码)在存入数据库时,为保护数据不被黑客或内鬼得到,通常也通过md5进行保护,这样原始数据只有用户自己知道。

注册时保存密码的md5值
登录时通过md5匹配用户

破解与盐

这种方式对密码的保护有一定的作用,但由于相同的数据其散列值也是相同的,所以如果用户使用类似123456等常见的弱密码注册,当数据库被拖库后,黑客得到弱密码的md5值仍可通过弱密码表得到原始密码。像cmd5这类破解网站,其实就是维护了一大批常见的密码或字符组合对应的md5值,查询的时候通过比对md5,进而得到原始数据。

cmd5类网站破解流程

为保护密码,一方面需要让用户尽量使用复杂密码,另一方面还通常对原数据作加盐处理。即对原始信息作一些混淆处理将弱密码变成强密码,但要注意保护这个操作不被破解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package net.wisedream.encrypt;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.util.Asserts;

/**
* 加盐md5示例
*/
public class HelloTest {
public static void main(String[] args) {
registerWithSalt("hello123", "worldabcd");
}

public static void registerWithSalt(String userName, String plainPassword) {
Asserts.check(plainPassword.length() > 6 &&
userName.length() > 6 &&
plainPassword.length() > userName.length(),
"长度不合要求");
//加盐操作
StringBuilder sb = new StringBuilder();
for (int i = 0; i < userName.toCharArray().length; i++) {
sb.append(i % 2 == 0 ? userName.charAt(i) : plainPassword.charAt(i));
}
sb.append(plainPassword.substring(plainPassword.length() / 2, plainPassword.length()));
//md5
String saltedMd5 = DigestUtils.md5Hex(sb.toString());
//todo 保存到数据库
System.out.println(String.format("保存用户名: %s, 密码: %s", userName, saltedMd5));
}
}

SHA(Secure Hash Algorithm)

SHA作为MD5的替代者,其用途与MD5相同。它与MD5不同之处主要在于摘要长度,它比MD5摘要(128位)更长,所以安全性较高。
sha系列算法长度

commons-codec提供了对SHA系列算法的支持(DigestUtils.shaXXX方法),与MD5相似,不再赘述。

参考自《Java加密解密的艺术》