一種簡單的給MD5加鹽演算法

來源:互聯網
上載者:User

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

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

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

Java代碼  
  1. package com.zving.framework.security;   
  2.   
  3. import java.security.MessageDigest;   
  4. import java.util.Random;   
  5.   
  6. import org.apache.commons.codec.binary.Hex;   
  7.   
  8. /**  
  9.  * @author wyuch
     
  10.  * @email wyuch@zving.com
     
  11.  * @date 2011-12-31
     
  12.  */  
  13. public class PasswordUtil {   
  14.     /**
     
  15.      * 產生含有隨機鹽的密碼
     
  16.      */  
  17.     public static String generate(String password) {   
  18.         Random r = new Random();   
  19.         StringBuilder sb = new StringBuilder(16);   
  20.         sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));   
  21.         int len = sb.length();   
  22.         if (len < 16) {   
  23.             for (int i = 0; i < 16 - len; i++) {
      
  24.                 sb.append("0");   
  25.             }   
  26.         }   
  27.         String salt = sb.toString();   
  28.         password = md5Hex(password + salt);   
  29.         char[] cs = new char[48];
      
  30.         for (int i = 0; i < 48; i += 3) {
      
  31.             cs[i] = password.charAt(i / 3 * 2);   
  32.             char c = salt.charAt(i / 3);   
  33.             cs[i + 1] = c;   
  34.             cs[i + 2] = password.charAt(i / 3 * 2 + 1);
      
  35.         }   
  36.         return new String(cs);   
  37.     }   
  38.   
  39.     /**
     
  40.      * 校正密碼是否正確
     
  41.      */  
  42.     public static boolean verify(String password, String md5) {
      
  43.         char[] cs1 = new char[32];
      
  44.         char[] cs2 = new char[16];
      
  45.         for (int i = 0; i < 48; i += 3) {
      
  46.             cs1[i / 3 * 2] = md5.charAt(i);   
  47.             cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
      
  48.             cs2[i / 3] = md5.charAt(i + 1);   
  49.         }   
  50.         String salt = new String(cs2);   
  51.         return md5Hex(password + salt).equals(new String(cs1));
      
  52.     }   
  53.   
  54.     /**
     
  55.      * 擷取十六進位字串形式的MD5摘要
     
  56.      */  
  57.     public static String md5Hex(String src) {   
  58.         try {   
  59.             MessageDigest md5 = MessageDigest.getInstance("MD5");   
  60.             byte[] bs = md5.digest(src.getBytes());   
  61.             return new String(new Hex().encode(bs));
      
  62.         } catch (Exception e) {   
  63.             return null;   
  64.         }   
  65.     }   
  66.   
  67.     public static void main(String[] args) {
      
  68.         String password = generate("admin");   
  69.         System.out.println(verify("admin", password));   
  70.     }   
  71. }  
package com.zving.framework.security;import java.security.MessageDigest;import java.util.Random;import org.apache.commons.codec.binary.Hex;/** * @author wyuch * @email wyuch@zving.com * @date 2011-12-31 */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));}}

--------------------------回應樓下幾位的問題---------------------------
1、這是一個很簡單的演算法,和絕大部分加鹽演算法一樣,只是為了增加通過MD5資料庫反查(例如使用http://www.md5.cc/類似的反查資料庫)的難度,並不能保證絕對不可反查。

2、作者假定,能夠拿到一個應用中使用者密碼的MD5摘要的人,也能獲得該應用的密碼摘要形成的演算法,以及該演算法依賴的外部因素(持久層中的使用者ID、使用者名稱、加鹽規則、隨機資料等)。因此不管是什麼樣的演算法(例如多次MD5以及各種加鹽規則),破解者都可以通過將手頭的MD5反查庫根據你的演算法和資料重新運算一遍,以獲得針對你的使用者資料庫的專用的MD5反查庫,然後再逐一反查密碼明文。

3、考慮到使用者密碼驗證實際上是再次形成MD5摘要的過程,而使用者密碼驗證是一個經常發生的行為,會有高並發的情況。所以一般使用者密碼驗證不允許採用非常複雜和消耗效能的演算法,一次驗證消耗的CPU時間應在100毫秒之內。我們採用MD5或SHA1的原因一是久經考驗的摘要無法復原性;二是效能在允許的範圍之內(對於密碼這樣的短字串,一台普通PC每秒都可以運算上萬次)。

4、因為第3點提到的效能要求,所以根據你的演算法重新構建一個專用MD5反查庫的時間不會特別長(採用較好的機器和並行計算,可以在有意義的時間範圍之內重新形成有100億條以上記錄的MD5反查庫)。

5、根據以上2/3/4點,不管採用什麼加鹽演算法,只要演算法可以獲得,則破解者都可以破解任意一個使用者的密碼的明文(準確地說是驗證該密碼是否在MD5反查庫中存在,如果存在則能獲得明文。特別複雜的密碼在MD5反查庫中不會有記錄,因此即使不加鹽也無法破解)。

6、但多次MD5、打亂MD5結果的數位順序這些演算法的安全性要更差一些,便如多次MD5,不管你是幾次MD5,破解者只需要根據你演算法運算形成MD5反查庫,則一次性就破解了你的所有密碼。

7、本文中的演算法以及MD5(UserName+Password+Salt)、MD5(UserID+Password+Salt)等,是在密碼之外有引入了幹擾項(稱之為鹽),因此破解者必須為每一個密碼單獨形成一個MD5反查庫,代價就會非常高,要想破解所有密碼實際上已經不可行了。

8、但MD5(UserName+Password+Salt)要求UserName永遠不變,MD5(UserID+Password+Salt)要求使用者在使用者名稱之外必須有一個不變的UserID。這些額外的要求,導致其適用性沒有本文中的演算法好。

9、如果以上情況有一條你不需要考慮,則本演算法對你確實沒有什麼用處,就當作者自賣自誇灌水吧。因為本演算法有一點點聊勝於無的優勢,所以發個文章請大家斧正,對回帖的兄弟們表示感謝

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.