1. 程式人生 > >MD5帶鹽值的java加密演算法

MD5帶鹽值的java加密演算法

        我們直接看一張圖:


        不知看後,會有何感觸。現在的MD5密碼資料庫的資料量已經非常龐大了,大部分常用密碼都可以通過MD5摘要反向查詢到密碼明文。為了防止內部人員(能夠接觸到資料庫或者資料庫備份檔案的人員)和外部入侵者通過MD5反查密碼明文,更好地保護使用者的密碼和個人帳戶安全(一個使用者可能會在多個系統中使用同樣的密碼,因此涉及到使用者在其他網站和系統中的資料安全),需要對MD5摘要結果摻入其他資訊,稱之為加鹽。 

        加鹽的演算法有很多,考慮到加鹽的目的(防止擁有系統底層許可權的人員),想做到絕對不可反查是很困難的,需要有其他軟體或者硬體的協助,在很多場景下的實用性比較差。如果只是想增加反查的難度,倒是有很多方法可以選擇,一種便利的方法是md5(Password+UserName),即將使用者名稱和密碼字串相加再MD5,這樣的MD5摘要基本上不可反查。但有時候使用者名稱可能會發生變化,發生變化後密碼即不可用了(驗證密碼實際上就是再次計算摘要的過程)。

        因此我們做了一個非常簡單的演算法,每次儲存密碼到資料庫時,都生成一個隨機16位數字,將這16位數字和密碼相加再求MD5摘要,然後在摘要中再將這16位數字按規則摻入形成一個48位的字串。在驗證密碼時再從48位字串中按規則提取16位數字,和使用者輸入的密碼相加再MD5。按照這種方法形成的結果肯定是不可直接反查的,且同一個密碼每次儲存時形成的摘要也都是不同的。如以下程式碼所示:

package com.zving.framework.security;

import java.security.MessageDigest;
import java.util.Random;

import org.apache.commons.codec.binary.Hex;

public class PasswordUtil {
	/**
	 * 生成含有隨機鹽的密碼
	 */
	public static String generate(String password) {
		Random r = new Random();
 		StringBuilder sb = new StringBuilder(16);
 		sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
 		int len = sb.length();
 		if (len < 16) {
 			for (int i = 0; i < 16 - len; i++) {
 				sb.append("0");
 			}
 		}
 		String salt = sb.toString();
 		password = md5Hex(password + salt);
 		char[] cs = new char[48];
 		for (int i = 0; i < 48; i += 3) {
 			cs[i] = password.charAt(i / 3 * 2);
 			char c = salt.charAt(i / 3);
 			cs[i + 1] = c;
 			cs[i + 2] = password.charAt(i / 3 * 2 + 1);
 		}
		return new String(cs);
	}

	/**
	 * 校驗密碼是否正確
	 */
	public static boolean verify(String password, String md5) {
 		char[] cs1 = new char[32];
		char[] cs2 = new char[16];
		for (int i = 0; i < 48; i += 3) {
			cs1[i / 3 * 2] = md5.charAt(i);
			cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
			cs2[i / 3] = md5.charAt(i + 1);
		}
		String salt = new String(cs2);
		return md5Hex(password + salt).equals(new String(cs1));
	}

	/**
	 * 獲取十六進位制字串形式的MD5摘要
	 */
	public static String md5Hex(String src) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			byte[] bs = md5.digest(src.getBytes());
			return new String(new Hex().encode(bs));
		} catch (Exception e) {
			return null;
		}
	}

	public static void main(String[] args) {
		String password = generate("admin");
		System.out.println(verify("admin", password));
	}
}

        這是一個很簡單的演算法,和絕大部分加鹽演算法一樣,只是為了增加通過MD5資料庫反查的難度,並不能保證絕對不可反查。 但是在密碼之外有引入了干擾項(稱之為鹽),因此破解者必須為每一個密碼單獨形成一個MD5反查庫,代價還是會非常高的,要想破解所有密碼實際上已經不可行了。