1. 程式人生 > >JAVA密碼加密演算法.RSA演算法(非對稱加密演算法)和密碼加鹽MD5

JAVA密碼加密演算法.RSA演算法(非對稱加密演算法)和密碼加鹽MD5

密碼加鹽MD5

Message Digest Algorithm MD5(中文名為訊息摘要演算法第五版)為電腦保安領域廣泛使用的一種雜湊函式,用以提供訊息的完整性保護。
是計算機廣泛使用的雜湊演算法之一(又譯摘要演算法、雜湊演算法),主流程式語言普遍已有MD5實現。將資料(如漢字)運算為另一固定長度值.
MD5演算法具有以下特點:
1、壓縮性:任意長度的資料,算出的MD5值長度都是固定的。
2、容易計算:從原資料計算出MD5值很容易。
3、抗修改性:對原資料進行任何改動,哪怕只修改1個位元組,所得到的MD5值都有很大區別。
4、強抗碰撞:已知原資料和其MD5值,想找到一個具有相同MD5值的資料(即偽造資料)是非常困難的。
MD5應用


一致性驗證
典型應用是對一段資訊(Message)產生資訊摘要(Message-Digest),以防止被篡改
安全訪問認證
MD5還廣泛用於作業系統的登陸認證上,如Unix、各類BSD系統登入密碼、數字簽名等諸多方面。如在Unix系統中使用者的密碼是以MD5(或其它類似的演算法)經Hash運算後儲存在檔案系統中。當用戶登入的時候,系統把使用者輸入的密碼進行MD5 Hash運算,然後再去和儲存在檔案系統中的MD5值進行比較,進而確定輸入的密碼是否正確

鹽(Salt)
在密碼學中,是指通過在密碼任意固定位置插入特定的字串,讓雜湊後的結果和使用原始密碼的雜湊結果不相符,這種過程稱之為“加鹽”。
以上這句話是維基百科上對於 Salt 的定義,但是僅憑這句話還是很難理解什麼叫 Salt,以及它究竟起到什麼作用。
第一代密碼


早期的軟體系統或者網際網路應用,資料庫中設計使用者表的時候,大致是這樣的結構:
1
2
3
4
5
6
7 mysql> desc User;
±---------±-------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±-------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| PassWord | varchar(150) | NO | | | |
±---------±-------------±-----±----±--------±------+
資料儲存形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------+
| UserName | PassWord |
±---------±---------+
| lichao | 123 |
| akasuna | 456 |
±---------±---------+
主要的關鍵欄位就是這麼兩個,一個是登陸時的使用者名稱,對應的一個密碼,而且那個時候的使用者名稱是明文儲存的,如果你登陸時使用者名稱是 123,那麼資料庫裡存的就是 123。這種設計思路非常簡單,但是缺陷也非常明顯,資料庫一旦洩露,那麼所有使用者名稱和密碼都會洩露,後果非常嚴重。
第二代密碼

為了規避第一代密碼設計的缺陷,聰明的人在資料庫中不在儲存明文密碼,轉而儲存加密後的密碼,典型的加密演算法是 MD5 和 SHA1,其資料表大致是這樣設計的:
1
2
3
4
5
6
7 mysql> desc User;
±---------±-------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±-------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| PwdHash | char(32) | NO | | | |
±---------±-------------±-----±----±--------±------+
資料儲存形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------------------------------+
| UserName | PwdHash |
±---------±---------------------------------+
| lichao | 202cb962ac59075b964b07152d234b70 |
| akasuna | 250cf8b51c773f3f8dc8b4be867a9a02 |
±---------±---------------------------------+
假如你設定的密碼是 123,那麼資料庫中儲存的就是 202cb962ac59075b964b07152d234b70 或 40bd001563085fc35165329ea1ff5c5ecbdbbeef。當用戶登陸的時候,會把使用者輸入的密碼執行 MD5(或者 SHA1)後再和資料庫就行對比,判斷使用者身份是否合法,這種加密演算法稱為雜湊。
嚴格地說,這種演算法不能算是加密,因為理論上來說,它不能被解密。所以即使資料庫丟失了,但是由於資料庫裡的密碼都是密文,根本無法判斷使用者的原始密碼,所以後果也不算太嚴重。
第三代密碼
本來第二代密碼設計方法已經很不錯了,只要你密碼設定得稍微複雜一點,就幾乎沒有被破解的可能性。但是如果你的密碼設定得不夠複雜,被破解出來的可能性還是比較大的。
好事者收集常用的密碼,然後對他們執行 MD5 或者 SHA1,然後做成一個數據量非常龐大的資料字典,然後對洩露的資料庫中的密碼就行對比,如果你的原始密碼很不幸的被包含在這個資料字典中,那麼花不了多長時間就能把你的原始密碼匹配出來。這個資料字典很容易收集,CSDN 洩露的那 600w 個密碼,就是很好的原始素材。
於是,第三代密碼設計方法誕生,使用者表中多了一個欄位:
1
2
3
4
5
6
7
8 mysql> desc User;
±---------±------------±-----±----±--------±------+
| Field | Type | Null | Key | Default | Extra |
±---------±------------±-----±----±--------±------+
| UserName | varchar(50) | NO | | | |
| Salt | char(50) | NO | | | |
| PwdHash | char(32) | NO | | | |
±---------±------------±-----±----±--------±------+
資料儲存形式如下:
1
2
3
4
5
6
7 mysql> select * from User;
±---------±---------------------------±---------------------------------+
| UserName | Salt |PwdHash |
±---------±---------------------------±---------------------------------+
| lichao | 1ck12b13k1jmjxrg1h0129h2lj | 6c22ef52be70e11b6f3bcf0f672c96ce |
| akasuna | 1h029kh2lj11jmjxrg13k1c12b | 7128f587d88d6686974d6ef57c193628 |
±---------±---------------------------±---------------------------------+
Salt 可以是任意字母、數字、或是字母或數字的組合,但必須是隨機產生的,每個使用者的 Salt 都不一樣,使用者註冊的時候,資料庫中存入的不是明文密碼,也不是簡單的對明文密碼進行雜湊,而是 MD5( 明文密碼 + Salt),也就是說:
MD5(‘123’ + ‘1ck12b13k1jmjxrg1h0129h2lj’) = ‘6c22ef52be70e11b6f3bcf0f672c96ce’
MD5(‘456’ + ‘1h029kh2lj11jmjxrg13k1c12b’) = ‘7128f587d88d6686974d6ef57c193628’
由於加了 Salt,即便資料庫洩露了,但是由於密碼都是加了 Salt 之後的雜湊,壞人們的資料字典已經無法直接匹配,明文密碼被破解出來的概率也大大降低。
是不是加了 Salt 之後就絕對安全了呢?淡然沒有!壞人們還是可以他們資料字典中的密碼,加上我們洩露資料庫中的 Salt,然後雜湊,然後再匹配。但是由於我們的 Salt 是隨機產生的,假如我們的使用者資料表中有 30w 條資料,資料字典中有 600w 條資料,壞人們如果想要完全覆蓋的壞,他們加上 Salt 後再雜湊的資料字典資料量就應該是 300000* 6000000 = 1800000000000,一萬八千億啊,幹壞事的成本太高了吧。但是如果只是想破解某個使用者的密碼的話,只需為這 600w 條資料加上 Salt,然後雜湊匹配。可見 Salt 雖然大大提高了安全係數,但也並非絕對安全。
實際專案中,Salt 不一定要加在最前面或最後面,也可以插在中間嘛,也可以分開插入,也可以倒序,程式設計時可以靈活調整,都可以使破解的難度指數級增長。

在util工具包中建立一個PasswordUtil.java

package cn.xdl.ovls.util;
import java.security.MessageDigest;
import java.util.UUID;
public class PasswordUtil {
	//傳一個字串過來經過md5處理返回一個字串
	public static String md5(String s) {
	    try {
	    	//MessageDigest是封裝md5演算法的工具物件還支援SHA演算法
	        MessageDigest md = MessageDigest.getInstance("MD5");
	        //通過digest拿到的任意字串,得到的bates都是等長的
	        byte[] bytes = md.digest(s.getBytes("utf-8"));
	        //這裡輸出的都是亂碼
//	        System.out.println(new String(bytes));
	        //返回的toHex通過下面方法再處理
	        return toHex(bytes);
	    }
	    catch (Exception e) {
	        throw new RuntimeException(e);
	    }
	}
	private static String toHex(byte[] bytes) {
		//把toHex的字串把二進位制轉換成十六進位制
	    final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
	    StringBuilder ret = new StringBuilder(bytes.length * 2);
	    //迴圈判斷是為了補位操作
	    for (int i=0; i<bytes.length; i++) {
	        ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
	        ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
	    }
	    return ret.toString();
	}
	public static String salt(){
		//使用UUID通用唯一識別碼,取第一個-前面的值
		UUID uuid = UUID.randomUUID();
		String[] arr = uuid.toString().split("-");
		return arr[0];
	}
	public static void main(String[] args) {
		System.out.println(md5("123"));
		System.out.println(md5("1234567890"));
		UUID uuid = UUID.randomUUID();
		System.out.println(uuid);
	}
	
}

通過以下程式碼呼叫

//TODO 密碼加密處理
String salt = PasswordUtil.salt();
//通MD5加密處理
String md5Pwd = PasswordUtil.md5(password+salt);
//設定加密密碼
user.setPassword(md5Pwd);
//設定鹽值
user.setSalt(salt);