Java Md5 加密
阿新 • • 發佈:2019-01-24
Java 兩種加密
此處僅記錄,方便查閱
1.Spring 自帶加密
最常用的md5加密,但是有個隱患,不同的使用者使用同樣的密碼加密出來得到的字串都是相同的值。
//匯入包
import org.springframework.util.DigestUtils;
//對密碼進行 md5 加密
String md5Password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
2.Md5 + salt 鹽加密
我們知道,如果直接對密碼進行雜湊,那麼黑客(統稱那些有能力竊取使用者資料並企圖得到使用者密碼的人)可以對一個已知密碼進行雜湊,然後通過對比雜湊值得到某使用者的密碼。換句話說,雖然黑客不能取得某特定使用者的密碼,但他可以知道使用特定密碼的使用者有哪些。
加Salt可以一定程度上解決這一問題。所謂加Salt,就是加點“佐料”。其基本想法是這樣的——當用戶首次提供密碼時(通常是註冊時),由系統自動往這個密碼裡撒一些“佐料”,然後再雜湊。而當用戶登入時,系統為使用者提供的程式碼撒上同樣的“佐料”,然後雜湊,再比較雜湊值,已確定密碼是否正確。
這裡的“佐料”被稱作“Salt值”,這個值是由系統隨機生成的,並且只有系統知道。這樣,即便兩個使用者使用了同一個密碼,由於系統為它們生成的salt值不同,他們的雜湊值也是不同的。即便黑客可以通過自己的密碼和自己生成的雜湊值來找具有特定密碼的使用者,但這個機率太小了(密碼和salt值都得和黑客使用的一樣才行)。
下面詳細介紹一下加Salt雜湊的過程。介紹之前先強調一點,前面說過,驗證密碼時要使用和最初雜湊密碼時使用“相同的”佐料。所以Salt值是要存放在資料庫裡的。
一.陳述一下工作流程:
1.根據已有的密碼字串去生成一個密碼+鹽字串,可以將鹽的加密字串也存放在資料庫(看需求),
2.驗證時將提交的密碼字串進行同樣的加密再從資料庫中取得已有的鹽進行組合密碼+鹽的字串和已有的進行驗證
package com.mi.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
public class Md5SaltTool {
private static final String HEX_NUMS_STR="0123456789ABCDEF";
private static final Integer SALT_LENGTH = 12;
/**
* 將16進位制字串轉換成位元組陣列
* @param hex
* @return
*/
public static byte[] hexStringToByte(String hex) {
int len = (hex.length() / 2);
byte[] result = new byte[len];
char[] hexChars = hex.toCharArray();
for (int i = 0; i < len; i++) {
int pos = i * 2;
result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4
| HEX_NUMS_STR.indexOf(hexChars[pos + 1]));
}
return result;
}
/**
* 將指定byte陣列轉換成16進位制字串
* @param b
* @return
*/
public static String byteToHexString(byte[] b) {
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
hexString.append(hex.toUpperCase());
}
return hexString.toString();
}
/**
* 驗證口令是否合法
* @param password
* @param passwordInDb
* @return
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public static boolean validPassword(String password, String passwordInDb)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
//將16進位制字串格式口令轉換成位元組陣列
byte[] pwdInDb = hexStringToByte(passwordInDb);
//宣告鹽變數
byte[] salt = new byte[SALT_LENGTH];
//將鹽從資料庫中儲存的口令位元組陣列中提取出來
System.arraycopy(pwdInDb, 0, salt, 0, SALT_LENGTH);
//建立訊息摘要物件
MessageDigest md = MessageDigest.getInstance("MD5");
//將鹽資料傳入訊息摘要物件
md.update(salt);
//將口令的資料傳給訊息摘要物件
md.update(password.getBytes("UTF-8"));
//生成輸入口令的訊息摘要
byte[] digest = md.digest();
//宣告一個儲存資料庫中口令訊息摘要的變數
byte[] digestInDb = new byte[pwdInDb.length - SALT_LENGTH];
//取得資料庫中口令的訊息摘要
System.arraycopy(pwdInDb, SALT_LENGTH, digestInDb, 0, digestInDb.length);
//比較根據輸入口令生成的訊息摘要和資料庫中訊息摘要是否相同
if (Arrays.equals(digest, digestInDb)) {
//口令正確返回口令匹配訊息
return true;
} else {
//口令不正確返回口令不匹配訊息
return false;
}
}
/**
* 獲得加密後的16進位制形式口令
* @param password
* @return
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public static String getEncryptedPwd(String password)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
//宣告加密後的口令陣列變數
byte[] pwd = null;
//隨機數生成器
SecureRandom random = new SecureRandom();
//宣告鹽陣列變數 12
byte[] salt = new byte[SALT_LENGTH];
//將隨機數放入鹽變數中
random.nextBytes(salt);
//宣告訊息摘要物件
MessageDigest md = null;
//建立訊息摘要
md = MessageDigest.getInstance("MD5");
//將鹽資料傳入訊息摘要物件
md.update(salt);
//將口令的資料傳給訊息摘要物件
md.update(password.getBytes("UTF-8"));
//獲得訊息摘要的位元組陣列
byte[] digest = md.digest();
//因為要在口令的位元組陣列中存放鹽,所以加上鹽的位元組長度
pwd = new byte[digest.length + SALT_LENGTH];
//將鹽的位元組拷貝到生成的加密口令位元組陣列的前12個位元組,以便在驗證口令時取出鹽
System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH);
//將訊息摘要拷貝到加密口令位元組陣列從第13個位元組開始的位元組
System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length);
for(int i=0;i<pwd.length;i++){
System.out.print(pwd[i]);
}
//將位元組陣列格式加密後的口令轉化為16進位制字串格式的口令
return byteToHexString(pwd);
}
}
測試類如下:
package com.mi.util;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
public class Md5SaltTest {
private static Map users = new HashMap();
public static void main(String[] args){
String userName = "zyg";
String password = "123";
registerUser(userName,password);
userName = "changong";
password = "456";
registerUser(userName,password);
String loginUserId = "zyg";
String pwd = "1232";
try {
if(loginValid(loginUserId,pwd)){
System.out.println("歡迎登陸!!!");
}else{
System.out.println("口令錯誤,請重新輸入!!!");
}
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 註冊使用者
*
* @param userName
* @param password
*/
public static void registerUser(String userName,String password){
String encryptedPwd = null;
try {
encryptedPwd = Md5SaltTool.getEncryptedPwd(password);
users.put(userName, encryptedPwd);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 驗證登陸
*
* @param userName
* @param password
* @return
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static boolean loginValid(String userName,String password)
throws NoSuchAlgorithmException, UnsupportedEncodingException{
/*String loginUserId = "zyg";
String pwd = "1232";*/
String pwdInDb = (String)users.get(userName);
if(null!=pwdInDb){ // 該使用者存在
return Md5SaltTool.validPassword(password, pwdInDb);
}else{
System.out.println("不存在該使用者!!!");
return false;
}
}
}