1.根據已有的密碼字符串去生成一個密碼+鹽字符串,可以將鹽的加密字符串也存放在數據庫(看需求),
2.驗證時將提交的密碼字符串進行同樣的加密再從數據庫中取得已有的鹽進行組合密碼+鹽的字符串和已有的進行驗證
1 package com.hjp.exercise.md5test; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.NoSuchAlgorithmException; 5 import java.util.HashMap; 6 import java.util.Map; 7 8 public class Md5SaltTest { 9 10 private static Map users = new HashMap(); 11 12 public static void main(String[] args){ 13 String userName = "zyg"; 14 String password = "123"; 15 registerUser(userName,password); 16 17 userName = "changong"; 18 password = "456"; 19 registerUser(userName,password); 20 21 String loginUserId = "zyg"; 22 String pwd = "1232"; 23 try { 24 if(loginValid(loginUserId,pwd)){ 25 System.out.println("歡迎登陸!!!"); 26 }else{ 27 System.out.println("口令錯誤,請重新輸入!!!"); 28 } 29 } catch (NoSuchAlgorithmException e) { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 } catch (UnsupportedEncodingException e) { 33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } 36 } 37 38 /** 39 * 注冊用戶 40 * 41 * @param userName 42 * @param password 43 */ 44 public static void registerUser(String userName,String password){ 45 String encryptedPwd = null; 46 try { 47 encryptedPwd = Md5SaltTool.getEncryptedPwd(password); 48 users.put(userName, encryptedPwd); 49 50 } catch (NoSuchAlgorithmException e) { 51 // TODO Auto-generated catch block 52 e.printStackTrace(); 53 } catch (UnsupportedEncodingException e) { 54 // TODO Auto-generated catch block 55 e.printStackTrace(); 56 } 57 } 58 59 /** 60 * 驗證登陸 61 * 62 * @param userName 63 * @param password 64 * @return 65 * @throws UnsupportedEncodingException 66 * @throws NoSuchAlgorithmException 67 */ 68 public static boolean loginValid(String userName,String password) 69 throws NoSuchAlgorithmException, UnsupportedEncodingException{ 70 /*String loginUserId = "zyg"; 71 String pwd = "1232";*/ 72 String pwdInDb = (String)users.get(userName); 73 if(null!=pwdInDb){ // 該用戶存在 74 return Md5SaltTool.validPassword(password, pwdInDb); 75 }else{ 76 System.out.println("不存在該用戶!!!"); 77 return false; 78 } 79 } 80 }
1 package com.hjp.exercise.md5test; 2 3 import java.io.UnsupportedEncodingException; 4 import java.security.MessageDigest; 5 import java.security.NoSuchAlgorithmException; 6 import java.security.SecureRandom; 7 import java.util.Arrays; 8 9 public class Md5SaltTool { 10 11 private static final String HEX_NUMS_STR="0123456789ABCDEF"; 12 private static final Integer SALT_LENGTH = 12; 13 14 /** 15 * 將16進制字符串轉換成字節數組 16 * @param hex 17 * @return 18 */ 19 public static byte[] hexStringToByte(String hex) { 20 int len = (hex.length() / 2); 21 byte[] result = new byte[len]; 22 char[] hexChars = hex.toCharArray(); 23 for (int i = 0; i < len; i++) { 24 int pos = i * 2; 25 result[i] = (byte) (HEX_NUMS_STR.indexOf(hexChars[pos]) << 4 26 | HEX_NUMS_STR.indexOf(hexChars[pos + 1])); 27 } 28 return result; 29 } 30 31 /** 32 * 將指定byte數組轉換成16進制字符串 33 * @param b 34 * @return 35 */ 36 public static String byteToHexString(byte[] b) { 37 StringBuffer hexString = new StringBuffer(); 38 for (int i = 0; i < b.length; i++) { 39 String hex = Integer.toHexString(b[i] & 0xFF); 40 if (hex.length() == 1) { 41 hex = '0' + hex; 42 } 43 hexString.append(hex.toUpperCase()); 44 } 45 return hexString.toString(); 46 } 47 48 /** 49 * 驗證口令是否合法 50 * @param password 51 * @param passwordInDb 52 * @return 53 * @throws NoSuchAlgorithmException 54 * @throws UnsupportedEncodingException 55 */ 56 public static boolean validPassword(String password, String passwordInDb) 57 throws NoSuchAlgorithmException, UnsupportedEncodingException { 58 //將16進制字符串格式口令轉換成字節數組 59 byte[] pwdInDb = hexStringToByte(passwordInDb); 60 //聲明鹽變量 61 byte[] salt = new byte[SALT_LENGTH]; 62 //將鹽從數據庫中保存的口令字節數組中提取出來 63 System.arraycopy(pwdInDb, 0, salt, 0, SALT_LENGTH); 64 //創建消息摘要對象 65 MessageDigest md = MessageDigest.getInstance("MD5"); 66 //將鹽數據傳入消息摘要對象 67 md.update(salt); 68 //將口令的數據傳給消息摘要對象 69 md.update(password.getBytes("UTF-8")); 70 //生成輸入口令的消息摘要 71 byte[] digest = md.digest(); 72 //聲明一個保存數據庫中口令消息摘要的變量 73 byte[] digestInDb = new byte[pwdInDb.length - SALT_LENGTH]; 74 //取得數據庫中口令的消息摘要 75 System.arraycopy(pwdInDb, SALT_LENGTH, digestInDb, 0, digestInDb.length); 76 //比較根據輸入口令生成的消息摘要和數據庫中消息摘要是否相同 77 if (Arrays.equals(digest, digestInDb)) { 78 //口令正確返回口令匹配消息 79 return true; 80 } else { 81 //口令不正確返回口令不匹配消息 82 return false; 83 } 84 } 85 86 /** 87 * 獲得加密后的16進制形式口令 88 * @param password 89 * @return 90 * @throws NoSuchAlgorithmException 91 * @throws UnsupportedEncodingException 92 */ 93 public static String getEncryptedPwd(String password) 94 throws NoSuchAlgorithmException, UnsupportedEncodingException { 95 //聲明加密后的口令數組變量 96 byte[] pwd = null; 97 //隨機數生成器 98 SecureRandom random = new SecureRandom(); 99 //聲明鹽數組變量 12 100 byte[] salt = new byte[SALT_LENGTH]; 101 //將隨機數放入鹽變量中 102 random.nextBytes(salt); 103 104 //聲明消息摘要對象 105 MessageDigest md = null; 106 //創建消息摘要 107 md = MessageDigest.getInstance("MD5"); 108 //將鹽數據傳入消息摘要對象 109 md.update(salt); 110 //將口令的數據傳給消息摘要對象 111 md.update(password.getBytes("UTF-8")); 112 //獲得消息摘要的字節數組 113 byte[] digest = md.digest(); 114 115 //因為要在口令的字節數組中存放鹽,所以加上鹽的字節長度 116 pwd = new byte[digest.length + SALT_LENGTH]; 117 //將鹽的字節拷貝到生成的加密口令字節數組的前12個字節,以便在驗證口令時取出鹽 118 System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH); 119 //將消息摘要拷貝到加密口令字節數組從第13個字節開始的字節 120 System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length); 121 for(int i=0;i<pwd.length;i++){ 122 System.out.print(pwd[i]); 123 } 124 //將字節數組格式加密后的口令轉化為16進制字符串格式的口令 125 return byteToHexString(pwd); 126 } 127 }