了解:
- MD5加密,是屬於不可逆的。我們知道正常使用MD5加密技術,同一字符,加密后的16進制數是不變的,自從出現彩虹表,對於公司內部員工來說,可以反查數據,獲取不可能的權限,所以出現了salt算法。
代碼如下:
package com.md5.demo;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 普通的MD加密
* @author peaceliu
*
*/
public class MD5Utils {
/**
* 使用md5的算法進行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("沒有md5這個算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);// 16進制數字
// 如果生成數字未滿32位,需要前面補0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code += "0";
}
return md5code;
}
public static void main(String[] args) {
System.out.println(md5("lhp"));
}
}
- 一般使用的加鹽:
md5(Password+UserName),即將用戶名和密碼字符串相加再MD5,這樣的MD5摘要基本上不可反查。
但有時候用戶名可能會發生變化,發生變化后密碼即不可用了(驗證密碼實際上就是再次計算摘要的過程)。
- 因此我們做了一個非常簡單的加鹽算法,每次保存密碼到數據庫時,都生成一個隨機16位數字,將這16位數字和密碼相加再求MD5摘要,然后在摘要中再將這16位數字按規則摻入形成一個48位的字符串。
- 在驗證密碼時再從48位字符串中按規則提取16位數字,和用戶輸入的密碼相加再MD5。按照這種方法形成的結果肯定是不可直接反查的,且同一個密碼每次保存時形成的摘要也都是不同的。
代碼如下:
package com.md5.demo;
import java.security.MessageDigest;
import java.util.Random;
import org.apache.commons.codec.binary.Hex;
/**
* MD5加鹽加密
*/
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 password1 = generate("admin");
System.out.println("結果:" + password1 + " 長度:"+ password1.length());
// 解碼
System.out.println(verify("admin", password1));
// 加密+加鹽
String password2= generate("admin");
System.out.println("結果:" + password2 + " 長度:"+ password2.length());
// 解碼
System.out.println(verify("admin", password2));
}
結果: