浅析MD5的二次加密

前段时间看群里经常问起MD5的二次加密,今天就来总结一下,分享给大家。

定义:

MD5是一个安全的散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程不可逆;

两次MD5加密原因:

其主要原因是一次加密方式,在现在的技术条件下已经不是很安全了,虽然MD5加密是不可逆的,且解密MD5没有现成的算法,但是可以使用能用穷举法,把可能出现的明文,用MD5算法散列之后,把得到的散列值和原始的数据形成一个一对一的映射表,通过比在表中比破解密码的MD5算法散列值,通过匹配从映射表中找出破解密码所对应的原始明文。

两次MD5加密实现:

实现而两次MD5加密则采用的是。

用户端:PASS=MD5(明文+固定salt)

服务端:PASS=MD5(用户输入+随机salt)。

具体实现:

MD5后端加密:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//这里列出了所有的加密,后文只是使用了其中一部分
/**
 * md5加密程序
 */
public class MD5Util {
 
    private static final String salt = "1a2b3c4d";
 
    /**
     * 进行MD5加密
     * @param str
     * @return
     */
    public static String md5 (String str) {
        return DigestUtils.md5DigestAsHex(str.getBytes());
    }
 
    /**
     * 第一次加密
     * @param inputPass
     * @return
     */
    public static String inputPassFromPass(String inputPass) {
        String str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
        return md5(str);
    }
 
    /**
     * 第二次加密
     * @param fromPass
     * @param salt
     * @return
     */
    public static String fromPassToDBPass(String fromPass, String salt) {
        String str = "" + salt.charAt(0) + salt.charAt(2) + fromPass + salt.charAt(5) + salt.charAt(4);
        return md5(str);
    }
 
    /**
     * 执行两次MD5加密
     * @param inputPass
     * @param saltDB
     * @return
     */
    public static String inputPassToDBPass(String inputPass, String saltDB) {
        String formPass = inputPassFromPass(inputPass);
        String dbPass = fromPassToDBPass(formPass, saltDB);
        return dbPass;
    }
}

注册流程:

前端对用户输入的密码进行md5加密(固定的salt1) —— 将加密后的密码通过ajax传递到后端 —— 后端随机生成一个salt2 —— 使用生成salt对前端传过来的密码进行加密 —— 然后将加密后密码和salt2一起保存到数据库中;

代码实现:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//前端js代码,需要引进md5.min.js包
function doRegister() {
        var inputPass = $("#passwordReg").val();
        var salt = "1a2b3c4d";
        var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
        var userName = $("#usernameReg").val();
        var password = md5(str);
        var json = {
            nickName: userName,
            passWord: password
        };
        $.ajax({
            url: "/login/doRegister",
            type: "POST",
            contentType: "application/json;charset=UTF-8",
            dataType: 'json',
            data: JSON.stringify(json),
            success: function (data) {
                console.info(data);
                if (data.success) {
                    window.location.href = "/login/login";
                } else {
                    alert(data.message);
                }
            },
            error: function () {
                console.error("报错了");
            }
        });
 
//后端处理注册逻辑的代码
/**
 * 用户注册
 * @param user
 * @return
 */
public LoginResult registerUser(User user) {
    String userName = user.getNickName();
    String fromPass = user.getPassWord();
    String salt = createSalt(6);
 
    //判断是否重复注册
    User resultUser = getByUserName(userName);
    if (resultUser != null) {
        return new LoginResult(false, LoginStatEnum.COUNTEXIST);
    }
 
    //创建MD5的二次加密
    String passWord = MD5Util.fromPassToDBPass(fromPass,salt);
 
    //保存到数据库
    int result = loginDao.insertUser(userName, passWord, salt);
    if (result == 1) {
        //LoginResult自定义的一个类,处理返回值的。读者可直接返回string信息
        return new LoginResult(true, LoginStatEnum.REGISTERSUCCESS);
    } else {
        return new LoginResult(false, LoginStatEnum.REGISTERFAILE);
    }
}

登录流程:

前端对用户输入的密码进行md5加密(固定的salt1) —— 将加密后的密码传递到后端 —— 后端使用用户id从数据库中取出用户信息 —— 在用户信息中取出二次加密后的密码和注册时候随机产生的加盐salt2 —— 后端对前端传进来的密码结合数据库取出来的salt2再进行md5加密然后与数据库中存储的密码进行对比 —— 匹配一致登录成功,否则登录失败

代码实现:

//前端js代码,需要引进md5.min.js包

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
function doLogin() {
        var inputPass = $("#password").val();
        var salt = "1a2b3c4d";
        var str = "" + salt.charAt(0) + salt.charAt(2) + inputPass + salt.charAt(5) + salt.charAt(4);
        var userName = $("#username").val();
        var password = md5(str);
        var json = {nickName: userName,
                    passWord: password,
                    salt: salt};
        $.ajax({
            url: "/login/doLogin",
            type: "POST",
            contentType: "application/json;charset=UTF-8",
            dataType: 'json',
            data: JSON.stringify(json),
            success: function (data) {
                console.info(data);
                if (data.success) {
                    window.location.href = "/seckill/list";
                } else {
                    alert(data.message);
                }
            },
            error: function () {
                console.error("报错了");
            }
        });
    };
 /**
     * 登陆验证账号和密码
     *
     * @param user
     * @return
     */
    public LoginResult doLogin(User user) {
        String userName = user.getNickName();
        String fromPass = user.getPassWord();
 
        //判断账号是否存在
        User resultUser = getByUserName(userName);
        if (resultUser == null) {
            //账号不存在
            return new LoginResult(false, LoginStatEnum.COUNTNOEXIST);
        }
 
        //验证密码
        String dbPass = resultUser.getPassWord();
        String saltDB = resultUser.getSalt();
        String calcPass = MD5Util.fromPassToDBPass(fromPass, saltDB);
        if (!dbPass.equals(calcPass)) {
            //密码错误
            return new LoginResult(false, LoginStatEnum.PASSWORDERROR);
        }
 
        //账号和密码正确登陆成功
        return new LoginResult(true, LoginStatEnum.LOGINSUCCESS);
    }

总结:

用户端MD5是为了防止用户的明文密码在网络上进行传输。而服务端MD5当数据被盗,防止反查。当你的前端和数据库同时被盗取的话,其也无法通过反推得到明文密码的。