1 MD5簡介
1.1 概述
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言普遍已有MD5實現。將數據(如漢字)運算為另一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD2、MD3和MD4。
1.2 發展歷史
MD2
MD4
MD5
1.3 MD5算法原理
對於MD5算法可以簡要的概括為:MD5是以512位分組來處理輸入信息,且每一分組又被划分為16個32位子分組,經過了一系列的處理后,算法的輸出由四個32位分組組成,將這四個32位分組級聯后將生成一個128位散列值。
總體流程圖如下:
1.3.1 填充
1.3.2 初始化變量
1.3.3 處理分組數據
1.3.4 輸出
1.4 MD5應用
1.4.1 一致性驗證
大家都知道,地球上任何人都有自己獨一無二的指紋,這常常成為司法機關鑒別罪犯身份最值得信賴的方法;與之類似,MD5就可以為任何文件(不管其大小、格式、數量)產生一個同樣獨一無二的“數字指紋”,如果任何人對文件做了任何改動,其MD5值也就是對應的“數字指紋”都會發生變化。
1.4.2 數字簽名
MD5的典型應用是對一段Message(字節串)產生fingerprint(指紋),以防止被“篡改”。舉個例子,你將一段話寫在一個叫 readme.txt文件中,並對這個readme.txt產生一個MD5的值並記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內容,你對這個文件重新計算MD5時就會發現(兩個MD5值不相同)。如果再有一個第三方的認證機構,用MD5還可以防止文件作者的“抵賴”,這就是所謂的數字簽名應用。
1.4.3 安全訪問認證
MD5還廣泛用於操作系統的登陸認證上,如Unix、各類BSD系統登錄密碼、數字簽名等諸多方面。如在Unix系統中用戶的密碼是以MD5(或其它類似的算法)經Hash運算后存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼進行MD5 Hash運算,然后再去和保存在文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在並不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這可以避免用戶的密碼被具有系統管理員權限的用戶知道。MD5將任意長度的“字節串”映射為一個128bit的大整數,並且是通過該128bit反推原始字符串是困難的,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串,從數學原理上說,是因為原始的字符串有無窮多個,這有點象不存在反函數的數學函數。所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統中的md5()函數重新設一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就行了。
1.5 MD5特點
2 java代碼實現MD5摘要算法
2.1 MD5詳細實現代碼
1 package xin.dreaming.md5; 2 3 public class MD5 { 4 /* 5 *四個鏈接變量 6 */ 7 private final int A=0x67452301; 8 private final int B=0xefcdab89; 9 private final int C=0x98badcfe; 10 private final int D=0x10325476; 11 /* 12 *ABCD的臨時變量 13 */ 14 private int Atemp,Btemp,Ctemp,Dtemp; 15 16 /* 17 *常量ti 18 *公式:floor(abs(sin(i+1))×(2pow32) 19 */ 20 private final int K[]={ 21 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee, 22 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8, 23 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193, 24 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51, 25 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, 26 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905, 27 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681, 28 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60, 29 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05, 30 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244, 31 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92, 32 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314, 33 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391}; 34 /* 35 *向左位移數,計算方法未知 36 */ 37 private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7, 38 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 39 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10, 40 15,21,6,10,15,21,6,10,15,21,6,10,15,21}; 41 42 43 /* 44 *初始化函數 45 */ 46 private void init(){ 47 Atemp=A; 48 Btemp=B; 49 Ctemp=C; 50 Dtemp=D; 51 } 52 /* 53 *移動一定位數 54 */ 55 private int shift(int a,int s){ 56 return(a<<s)|(a>>>(32-s));//右移的時候,高位一定要補零,而不是補充符號位 57 } 58 /* 59 *主循環 60 */ 61 private void MainLoop(int M[]){ 62 int F,g; 63 int a=Atemp; 64 int b=Btemp; 65 int c=Ctemp; 66 int d=Dtemp; 67 for(int i = 0; i < 64; i ++){ 68 if(i<16){ 69 F=(b&c)|((~b)&d); 70 g=i; 71 }else if(i<32){ 72 F=(d&b)|((~d)&c); 73 g=(5*i+1)%16; 74 }else if(i<48){ 75 F=b^c^d; 76 g=(3*i+5)%16; 77 }else{ 78 F=c^(b|(~d)); 79 g=(7*i)%16; 80 } 81 int tmp=d; 82 d=c; 83 c=b; 84 b=b+shift(a+F+K[i]+M[g],s[i]); 85 a=tmp; 86 } 87 Atemp=a+Atemp; 88 Btemp=b+Btemp; 89 Ctemp=c+Ctemp; 90 Dtemp=d+Dtemp; 91 92 } 93 /* 94 *填充函數 95 *處理后應滿足bits≡448(mod512),字節就是bytes≡56(mode64) 96 *填充方式為先加一個0,其它位補零 97 *最后加上64位的原來長度 98 */ 99 private int[] add(String str){ 100 int num=((str.length()+8)/64)+1;//以512位,64個字節為一組 101 int strByte[]=new int[num*16];//64/4=16,所以有16個整數 102 for(int i=0;i<num*16;i++){//全部初始化0 103 strByte[i]=0; 104 } 105 int i; 106 for(i=0;i<str.length();i++){ 107 strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個字節,小端序 108 } 109 strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1 110 /* 111 *添加原長度,長度指位的長度,所以要乘8,然后是小端序,所以放在倒數第二個,這里長度只用了32位 112 */ 113 strByte[num*16-2]=str.length()*8; 114 return strByte; 115 } 116 /* 117 *調用函數 118 */ 119 public String getMD5(String source){ 120 init(); 121 int strByte[]=add(source); 122 for(int i=0;i<strByte.length/16;i++){ 123 int num[]=new int[16]; 124 for(int j=0;j<16;j++){ 125 num[j]=strByte[i*16+j]; 126 } 127 MainLoop(num); 128 } 129 return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp); 130 131 } 132 /* 133 *整數變成16進制字符串 134 */ 135 private String changeHex(int a){ 136 String str=""; 137 for(int i=0;i<4;i++){ 138 str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0'); 139 140 } 141 return str; 142 } 143 /* 144 *單例 145 */ 146 private static MD5 instance; 147 public static MD5 getInstance(){ 148 if(instance==null){ 149 instance=new MD5(); 150 } 151 return instance; 152 } 153 154 private MD5(){}; 155 156 public static void main(String[] args){ 157 String str=MD5.getInstance().getMD5("123"); 158 System.out.println(str); 159 } 160 }
代碼摘要結果:
此處代碼可以參考前邊MD5原理流程,了解其代碼執行流程。
2.2 使用java.security.MessageDigest實現MD5摘要算法
2.2.1代碼實現:
1 /** 2 * md5計算. 3 * 4 * @param datas 5 * 待計算的數據 6 * @return 計算結果 7 */ 8 public static byte[] md5(byte[] datas) { 9 MessageDigest md = null; 10 try { 11 md = MessageDigest.getInstance("MD5"); 12 md.reset(); 13 md.update(datas); 14 return md.digest(); 15 } catch (Exception e) { 16 LogUtil.writeErrorLog("MD5計算失敗", e); 17 return null; 18 } 19 }
2.2.2 MessageDigest 簡要說明
MessageDigest 通過其getInstance系列靜態函數來進行實例化和初始化。MessageDigest 對象通過使用 update
方法處理數據。任何時候都可以調用 reset
方法重置摘要。一旦所有需要更新的數據都已經被更新了,應該調用 digest
方法之一完成哈希計算並返回結果。
對於給定數量的更新數據,digest
方法只能被調用一次。digest
方法被調用后,MessageDigest 對象被重新設置成其初始狀態。
MessageDigest 的實現可隨意選擇是否實現 Cloneable 接口。客戶端應用程可以通過嘗試復制和捕獲 CloneNotSupportedException 測試可復制性:
參考:
1、https://baike.baidu.com/item/MD5/212708?fr=aladdin#1_3
2、http://hubingforever.blog.163.com/blog/static/171040579201210781650340/