MD5分析與代碼實現
一、 MD5密碼算法的特點
(1) 輸出總為16字節
(2) 不可逆性
(3) 高度離散性
(4) 抗碰撞性
二、 常用實例
(1) 密碼保護
(2) 文件完整性校驗【用於抗碰撞性高,可用於下載文件時,查看文件的md5於下載后,在檢驗一次md5值,如果相同,則文件沒有被修改,否則文件被修改了。從而實現文件完整性校驗】
(3) 數字簽名
(4) 雲盤秒傳【有時雲盤上傳時,在傳輸大文件的情況下,並不是真正實現秒傳。其實只需計算一次文件的Md5值,然后再數據庫中進行尋找,如果有相同的Md5,即直接引用即可】
三、 MD5算法的過程實現如下
(1) 數據補充
MD5密碼算法,需要明文的長度為N*512+448bit,即數據的長度除以512位為448bit。但總長度要是512bit。其實很好奇,最后64bit去哪了。其實最后64bit需要單獨補充,作用是用來計算原始的數據長度。其次,補充的位數為1-521之間。補充的第一位為1,其次補充0.直到滿足mod512的結果為448。圖示如下。
(2)標准幻數
標准幻數總共為4*4=16字節,看到這個16字節,是不是感覺到很熟悉。對的,它就跟MD5輸出16字節息息相關。
A = 01234567 B = 89ABCDEF
C = FEDCBA98 D = 76543210
這就是開始的標准幻數,這些就是作為循環計算的初始值用的。但需要注意的是,在程序中用的是小端字節序,也就是說高字節在前,低字節在后。即為如下所示
A = 0x67452301 B = 0xEFCDB89
C = 0x98BADCFE D = 0x10325476
(3) 加密步驟
1.原始數據分為n個512位,即n個64字節
2.然后對每個512bit,即64字節分為16個4字節的數據。
4字節x[0] |
4字節x[1] |
4字節x[2] |
4字節x[3] |
4字節x[4] |
4字節x[5] |
4字節x[6] |
4字節x[7] |
4字節x[8] |
4字節x[9] |
4字節x[10] |
4字節x[11] |
4字節x[12] |
4字節x[13] |
4字節x[14] |
4字節x[15] |
3.准備4個函數
FF(A,B,C,D, X, S,AC);GG(A,B,C,D, X, S,AC);
HH(A,B,C,D, X, S,AC);II(A,B,C,D, X, S,AC);
4個標准幻數 分組的每4字節 固定常數
通過4個函數的邏輯運算於移位運算,改變第一個標准幻數的值,對於每4個字節,4個函數分別執行16次。
FF (A,B,C,D,x[0],S11,AC)
FF (D,A,B,C,x[1],S12,AC)
……..
GG (A,B,C,D,x[0],S21,AC)
……
HH (A,B,C,D,x[0],S31,AC)
……
II (A,B,C,D,x[0],S41,AC)
……
GG (A,B,C,D,x[0],S44,AC)
這就是一輪計算,一輪計算后,改變了4個標准幻數的值。形成新的A,B,C,D。然后新的A,B,C,D於原來的A,B,C,D相加,從而形成新的標准幻數。
整個過程重復進行,知道所有的64字節處理完,得到最后的標准幻數。得到的最后的標准幻數即為MD5值。
例:
A = 0Xbfcbfgce B = 0Xc4105a80
C = 0X02599277 D = 0X5c099ee2
MD5 = cefgcbbf805a10c477925202e29e095c
一、 C++代碼實現如下
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 #define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的時候,高位一定要補零,而不是補充符號位 5 #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) 6 #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) 7 #define H(x, y, z) ((x) ^ (y) ^ (z)) 8 #define I(x, y, z) ((y) ^ ((x) | (~z))) 9 #define A 0x67452301 10 #define B 0xefcdab89 11 #define C 0x98badcfe 12 #define D 0x10325476 13 //strBaye的長度 14 unsigned int strlength; 15 //A,B,C,D的臨時變量 16 unsigned int atemp; 17 unsigned int btemp; 18 unsigned int ctemp; 19 unsigned int dtemp; 20 //常量ti unsigned int(abs(sin(i+1))*(2pow32)) 21 const unsigned int k[]={ 22 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee, 23 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8, 24 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193, 25 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51, 26 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, 27 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905, 28 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681, 29 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60, 30 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05, 31 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244, 32 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92, 33 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314, 34 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391}; 35 //向左位移數 36 const unsigned int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7, 37 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 38 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10, 39 15,21,6,10,15,21,6,10,15,21,6,10,15,21}; 40 const char str16[]="0123456789abcdef"; 41 void mainLoop(unsigned int M[]) 42 { 43 unsigned int f,g; 44 unsigned int a=atemp; 45 unsigned int b=btemp; 46 unsigned int c=ctemp; 47 unsigned int d=dtemp; 48 for (unsigned int i = 0; i < 64; i++) 49 { 50 if(i<16){ 51 f=F(b,c,d); 52 g=i; 53 }else if (i<32) 54 { 55 f=G(b,c,d); 56 g=(5*i+1)%16; 57 }else if(i<48){ 58 f=H(b,c,d); 59 g=(3*i+5)%16; 60 }else{ 61 f=I(b,c,d); 62 g=(7*i)%16; 63 } 64 unsigned int tmp=d; 65 d=c; 66 c=b; 67 b=b+shift((a+f+k[i]+M[g]),s[i]); 68 a=tmp; 69 } 70 atemp=a+atemp; 71 btemp=b+btemp; 72 ctemp=c+ctemp; 73 dtemp=d+dtemp; 74 } 75 /* 76 *填充函數 77 *處理后應滿足bits≡448(mod512),字節就是bytes≡56(mode64) 78 *填充方式為先加一個1,其它位補零 79 *最后加上64位的原來長度 80 */ 81 unsigned int* add(string str) 82 { 83 unsigned int num=((str.length()+8)/64)+1;//以512位,64個字節為一組 84 unsigned int *strByte=new unsigned int[num*16]; //64/4=16,所以有16個整數 85 strlength=num*16; 86 for (unsigned int i = 0; i < num*16; i++) 87 strByte[i]=0; 88 for (unsigned int i=0; i <str.length(); i++) 89 { 90 strByte[i>>2]|=(str[i])<<((i%4)*8);//一個整數存儲四個字節,i>>2表示i/4 一個unsigned int對應4個字節,保存4個字符信息 91 } 92 strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1 一個unsigned int保存4個字符信息,所以用128左移 93 /* 94 *添加原長度,長度指位的長度,所以要乘8,然后是小端序,所以放在倒數第二個,這里長度只用了32位 95 */ 96 strByte[num*16-2]=str.length()*8; 97 return strByte; 98 } 99 string changeHex(int a) 100 { 101 int b; 102 string str1; 103 string str=""; 104 for(int i=0;i<4;i++) 105 { 106 str1=""; 107 b=((a>>i*8)%(1<<8))&0xff; //逆序處理每個字節 108 for (int j = 0; j < 2; j++) 109 { 110 str1.insert(0,1,str16[b%16]); 111 b=b/16; 112 } 113 str+=str1; 114 } 115 return str; 116 } 117 string getMD5(string source) 118 { 119 atemp=A; //初始化 120 btemp=B; 121 ctemp=C; 122 dtemp=D; 123 unsigned int *strByte=add(source); 124 for(unsigned int i=0;i<strlength/16;i++) 125 { 126 unsigned int num[16]; 127 for(unsigned int j=0;j<16;j++) 128 num[j]=strByte[i*16+j]; 129 mainLoop(num); 130 } 131 return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp)); 132 } 133 134 int main() 135 { 136 string ss; 137 // cin>>ss; 138 string s=getMD5("abc"); 139 cout<<s; 140 return 0; 141 }