MD5加密演算法原理及實現
MD5訊息摘要演算法,屬Hash演算法一類。MD5演算法對輸入任意長度的訊息進行執行,產生一個128位的訊息摘要。
以下所描述的訊息長度、填充資料都以位(Bit)為單位,位元組序為小端位元組。
演算法原理
1、資料填充
對訊息進行資料填充,使訊息的長度對512取模得448,設訊息長度為X,即滿足X mod 512=448。根據此公式得出需要填充的資料長度。
填充方法:在訊息後面進行填充,填充第一位為1,其餘為0。
2、新增訊息長度
在第一步結果之後再填充上原訊息的長度,可用來進行的儲存長度為64位。如果訊息長度大於264,則只使用其低64位的值,即(訊息長度 對 264取模)。
在此步驟進行完畢後,最終訊息長度就是512的整數倍。
3、資料處理
準備需要用到的資料:
- 4個常數: A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;
- 4個函式:F(X,Y,Z)=(X & Y) | ((~X) & Z); G(X,Y,Z)=(X & Z) | (Y & (~Z)); H(X,Y,Z)=X ^ Y ^ Z; I(X,Y,Z)=Y ^ (X | (~Z));
把訊息分以512位為一分組進行處理,每一個分組進行4輪變換,以上面所說4個常數為起始變數進行計算,重新輸出4個變數,以這4個變數再進行下一分組的運算,如果已經是最後一個分組,則這4個變數為最後的結果,即MD5值。
具體計算的實現較為複雜,建議查閱相關書籍,下面給出在C++上的實現程式碼。
程式碼實現
#MD5.h
1 #ifndef MD5H 2 #define MD5H 3 #include <math.h> 4 #include <Windows.h> 5 6 void ROL(unsigned int &s, unsigned short cx); //32位數迴圈左移實現函式 7 void ltob(unsigned int &i); //B\L互轉,接受UINT型別 8 unsigned int* MD5(const char* mStr); //介面函式,並執行資料填充,計算MD5時呼叫此函式 9 10 #endif
#MD5.cpp
1 #include "MD5.h" 2 3 /*4組計算函式*/ 4 inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) 5 { 6 return (X & Y) | ((~X) & Z); 7 } 8 inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) 9 { 10 return (X & Z) | (Y & (~Z)); 11 } 12 inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) 13 { 14 return X ^ Y ^ Z; 15 } 16 inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) 17 { 18 return Y ^ (X | (~Z)); 19 } 20 /*4組計算函式結束*/ 21 22 /*32位數迴圈左移實現函式*/ 23 void ROL(unsigned int &s, unsigned short cx) 24 { 25 if (cx > 32)cx %= 32; 26 s = (s << cx) | (s >> (32 - cx)); 27 return; 28 } 29 30 /*B\L互轉,接收UINT型別*/ 31 void ltob(unsigned int &i) 32 { 33 unsigned int tmp = i;//儲存副本 34 byte *psour = (byte*)&tmp, *pdes = (byte*)&i; 35 pdes += 3;//調整指標,準備左右調轉 36 for (short i = 3; i >= 0; --i) 37 { 38 CopyMemory(pdes - i, psour + i, 1); 39 } 40 return; 41 } 42 43 /* 44 MD5迴圈計算函式,label=第幾輪迴圈(1<=label<=4),lGroup陣列=4個種子副本,M=資料(16組32位數指標) 45 種子陣列排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B; 46 */ 47 void AccLoop(unsigned short label, unsigned int *lGroup, void *M) 48 { 49 unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定義:4個指標; T表累加器; 區域性變數 50 typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定義函式型別 51 const unsigned int rolarray[4][4] = { 52 { 7, 12, 17, 22 }, 53 { 5, 9, 14, 20 }, 54 { 4, 11, 16, 23 }, 55 { 6, 10, 15, 21 } 56 };//迴圈左移-位數表 57 const unsigned short mN[4][16] = { 58 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 59 { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 }, 60 { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 }, 61 { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 } 62 };//資料座標表 63 const unsigned int *pM = static_cast<unsigned int*>(M);//轉換型別為32位的Uint 64 TAcc = ((label - 1) * 16) + 1; //根據第幾輪迴圈初始化T表累加器 65 clac clacArr[4] = { F, G, H, I }; //定義並初始化計算函式指標陣列 66 67 /*一輪迴圈開始(16組->16次)*/ 68 for (short i = 0; i < 16; ++i) 69 { 70 /*進行指標自變換*/ 71 i1 = lGroup + ((0 + i) % 4); 72 i2 = lGroup + ((3 + i) % 4); 73 i3 = lGroup + ((2 + i) % 4); 74 i4 = lGroup + ((1 + i) % 4); 75 76 /*第一步計算開始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接計算T表*/ 77 tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i))))); 78 ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:迴圈左移 79 *i1 = *i2 + tmpi;//第三步:相加並賦值到種子 80 } 81 return; 82 } 83 84 /*介面函式,並執行資料填充*/ 85 unsigned int* MD5(const char* mStr) 86 { 87 unsigned int mLen = strlen(mStr); //計算字串長度 88 if (mLen < 0) return 0; 89 unsigned int FillSize = 448 - ((mLen * 8) % 512); //計算需填充的bit數 90 unsigned int FSbyte = FillSize / 8; //以位元組表示的填充數 91 unsigned int BuffLen = mLen + 8 + FSbyte; //緩衝區長度或者說填充後的長度 92 unsigned char *md5Buff = new unsigned char[BuffLen]; //分配緩衝區 93 CopyMemory(md5Buff, mStr, mLen); //複製字串到緩衝區 94 95 /*資料填充開始*/ 96 md5Buff[mLen] = 0x80; //第一個bit填充1 97 ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函式為FillMemory 98 unsigned long long lenBit = mLen * 8ULL; //計算字串長度,準備填充 99 CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充長度 100 /*資料填充結束*/ 101 102 /*運算開始*/ 103 unsigned int LoopNumber = BuffLen / 64; //以16個字為一分組,計算分組數量 104 unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4個種子,小端型別 105 unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //種子副本陣列,並作為返回值返回 106 for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分組大迴圈開始 107 { 108 /*進入4次計算的小迴圈*/ 109 for (unsigned short Lcount = 0; Lcount < 4;) 110 { 111 AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]); 112 } 113 /*資料相加作為下一輪的種子或者最終輸出*/ 114 A = (lGroup[0] += A); 115 B = (lGroup[3] += B); 116 C = (lGroup[2] += C); 117 D = (lGroup[1] += D); 118 } 119 /*轉換記憶體中的佈局後才能正常顯示*/ 120 ltob(lGroup[0]); 121 ltob(lGroup[1]); 122 ltob(lGroup[2]); 123 ltob(lGroup[3]); 124 delete[] md5Buff; //清除記憶體並返回 125 return lGroup; 126 }
再給出呼叫例項(以win32控制檯應用程式為例):
#main.cpp
1 #include <iostream> 2 #include <string.h> 3 #include <stdlib.h> 4 #include "MD5.h" 5 6 int main(int argc, char **argv) 7 { 8 char tmpstr[256], buf[4][10]; 9 std::cout << "請輸入要加密的字串:"; 10 std::cin >> tmpstr; 11 unsigned int* tmpGroup = MD5(tmpstr); 12 sprintf_s(buf[0], "%8X", tmpGroup[0]); 13 sprintf_s(buf[1], "%8X", tmpGroup[3]); 14 sprintf_s(buf[2], "%8X", tmpGroup[2]); 15 sprintf_s(buf[3], "%8X", tmpGroup[1]); 16 std::cout <<"MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl; 17 18 delete[] tmpGroup; 19 return 0; //在此下斷點才能看到輸出的值 20 }
注:以上程式碼在VS2013上編譯通過