MD5加密概述,原理及实现
MD5概述:
MD5消息摘要算法,屬Hash算法一類。MD5算法對輸入任意長度的消息進行運行,產生一個128位的消息摘要(32位的數字字母混合碼)。
MD5主要特點:
不可逆,相同數據的MD5值肯定一樣,不同數據的MD5值不一樣
(一個MD5理論上的確是可能對應無數多個原文的,因為MD5是有限多個的而原文可以是無數多個。比如主流使用的MD5將任意長度的“字節串映射為一個128bit的大整數。也就是一共有2^128種可能,大概是3.4*10^38,這個數字是有限多個的,而但是世界上可以被用來加密的原文則會有無數的可能性)
MD5的性質:
1、壓縮性:任意長度的數據,算出的MD5值長度都是固定的(相當于超損壓縮)。
2、容易計算:從原數據計算出MD5值很容易。
3、抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。
4、弱抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即偽造數據)是非常困難的。
5、強抗碰撞:想找到兩個不同的數據,使它們具有相同的MD5值,是非常困難的。
雖說MD5有不可逆的特點
但是由于某些MD5破解網站,專門用來查詢MD5碼,其通過
把常用的密碼先MD5處理,并將數據存儲起來,然后跟需要查詢的MD5結果匹配,這時就有可能通過匹配的MD5得到明文,所以有些簡單的MD5碼是反查到加密前原文的。
為了讓MD5碼更加安全,涌現了很多其他方法,如加鹽。 鹽要足夠長足夠亂 得到的MD5碼就很難查到。
MD5用途:
1.防止被篡改:
1)比如發送一個電子文檔,發送前,我先得到MD5的輸出結果a。然后在對方收到電子文檔后,對方也得到一個MD5的輸出結果b。如果a與b一樣就代表中途未被篡改。
2)比如我提供文件下載,為了防止不法分子在安裝程序中添加木馬,我可以在網站上公布由安裝文件得到的MD5輸出結果。
3)SVN在檢測文件是否在CheckOut后被修改過,也是用到了MD5.
2.防止直接看到明文:
現在很多網站在數據庫存儲用戶的密碼的時候都是存儲用戶密碼的MD5值。這樣就算不法分子得到數據庫的用戶密碼的MD5值,也無法知道用戶的密碼。(比如在UNIX系統中用戶的密碼就是以MD5(或其它類似的算法)經加密后存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼計算成MD5值,然后再去和保存在文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這不但可以避免用戶的密碼被具有系統管理員權限的用戶知道,而且還在一定程度上增加了密碼被破解的難度。)
3.防止抵賴(數字簽名):
這需要一個第三方認證機構。例如A寫了一個文件,認證機構對此文件用MD5算法產生摘要信息并做好記錄。若以后A說這文件不是他寫的,權威機構只需對此文件重新產生摘要信息,然后跟記錄在冊的摘要信息進行比對,相同的話,就證明是A寫的了。這就是所謂的“數字簽名”。
MD5加密算法原理及實現:
MD5算法原理:
1、數據填充
對消息進行數據填充,使消息的長度對512取模得448,設消息長度為X,即滿足X mod 512=448。根據此公式得出需要填充的數據長度。
填充方法:在消息后面進行填充,填充第一位為1,其余為0。
(此時消息長度為N*512+448)
2、添加消息長度
在第一步結果之后再填充上原消息的長度,可用來進行的存儲長度為64位。如果消息長度大于264,則只使用其低64位的值,即(消息長度 對 264取模)。
在此步驟進行完畢后,最終消息長度就是512的整數倍。
(此時消息長度為(N+1)*512?)
3、數據處理
首先需要用到4個常數:
四個32位變量初始化(經過研究所得,為固定值)
A = 0×01234567?
B = 0×89ABCDEF?
C = 0xFEDCBA98?
D = 0×76543210??
它們稱為鏈接變量(chaining?variable)
?
以上為標準的幻數的(物理順序)
如果在程序中(小端模式)定義應該是:?
A = 0x67452301
B = 0xEFCDAB89
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));
(&是與, |是或, ~是非, ^是異或)
?
然后是4種操作
FF(a,b,c,d,Mi,s,tj) ?表示a=b+((a+(F(b,c,d)+Mi+tj)<<< s)
GG(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(G(b,c,d)+Mi+tj)<<<?s)?
HH(a,b,c,d,Mi,s,tj) 表示 a=b+((a+(H(b,c,d)+Mi+tj)<<<?s)?
II(a,b,c,d,Mi,s,tj)?? 表示a=b+((a+(I(b,c,d)+Mi+tj)<<<?s)?
其中Mi表示消息的第i個子分組(從0到15,共16個),<<<?s表示循環左移s位
常數tj為:?
在第j步中,tj是4294967296*abs(sin(j))的整數部分,i的單位是弧度。
(4294967296是2的32次方)
亦可用 0x100000000UL * abs(sin((double)j)) 計算
?
x循環左移s位:
( s << x ) | ( s >> (32 - x) )
?
4.MD5運算:
由類似的64次循環構成,分成4輪,每輪16次。每輪使用FF,GG,HH,II中的一種操作;
一輪中,a,b,c,d的使用順序輪轉;
例如第一輪:
第一次計算 FF(a,b,c,d,M0,s,t1)
? ? ? ? ? ? ? ? ? ?a = a+(F(b,c,d)+M0+t1);
? ? ? ? ? ? ? ? ? ?a = ( s <<a ) | ( s >> (32 - a) );
? ? ? ? ? ? ? ? ? ?a = a + b;
第二次計算 FF(b,c,d,a,M1,s,t2)
? ? ? ? ? ? ? ? ? ?b = b+(F(c,d,a)+M1+t2);
? ? ? ? ? ? ? ? ? ?b = ( s <<b ) | ( s >> (32 - b) );
? ? ? ? ? ? ? ? ? ?b = b + c;
?
這4輪共64步是:?
(初次使用的a,b,c,d為A,B,C,D的值,而Mi,s,tj根據下面的數值進行使用,可認為是常量,)
第一輪?
FF(a,b,c,d,M0,7,0xd76aa478)?
FF(d,a,b,c,M1,12,0xe8c7b756)?
FF(c,d,a,b,M2,17,0×242070db)?
FF(b,c,d,a,M3,22,0xc1bdceee)?
FF(a,b,c,d,M4,7,0xf57c0faf)?
FF(d,a,b,c,M5,12,0×4787c62a)?
FF(c,d,a,b,M6,17,0xa8304613)?
FF(b,c,d,a,M7,22,0xfd469501)?
FF(a,b,c,d,M8,7,0×698098d8)?
FF(d,a,b,c,M9,12,0×8b44f7af)?
FF(c,d,a,b,M10,17,0xffff5bb1)?
FF(b,c,d,a,M11,22,0×895cd7be)?
FF(a,b,c,d,M12,7,0×6b901122)?
FF(d,a,b,c,M13,12,0xfd987193)?
FF(c,d,a,b,M14,17,0xa679438e)?
FF(b,c,d,a,M15,22,0×49b40821)?
?
第二輪?
GG(a,b,c,d,M1,5,0xf61e2562)?
GG(d,a,b,c,M6,9,0xc040b340)?
GG(c,d,a,b,M11,14,0×265e5a51)?
GG(b,c,d,a,M0,20,0xe9b6c7aa)?
GG(a,b,c,d,M5,5,0xd62f105d)?
GG(d,a,b,c,M10,9,0×02441453)?
GG(c,d,a,b,M15,14,0xd8a1e681)?
GG(b,c,d,a,M4,20,0xe7d3fbc8)?
GG(a,b,c,d,M9,5,0×21e1cde6)?
GG(d,a,b,c,M14,9,0xc33707d6)?
GG(c,d,a,b,M3,14,0xf4d50d87)?
GG(b,c,d,a,M8,20,0×455a14ed)?
GG(a,b,c,d,M13,5,0xa9e3e905)?
GG(d,a,b,c,M2,9,0xfcefa3f8)?
GG(c,d,a,b,M7,14,0×676f02d9)?
GG(b,c,d,a,M12,20,0×8d2a4c8a)?
?
第三輪?
HH(a,b,c,d,M5,4,0xfffa3942)?
HH(d,a,b,c,M8,11,0×8771f681)?
HH(c,d,a,b,M11,16,0×6d9d6122)?
HH(b,c,d,a,M14,23,0xfde5380c)?
HH(a,b,c,d,M1,4,0xa4beea44)?
HH(d,a,b,c,M4,11,0×4bdecfa9)?
HH(c,d,a,b,M7,16,0xf6bb4b60)?
HH(b,c,d,a,M10,23,0xbebfbc70)?
HH(a,b,c,d,M13,4,0×289b7ec6)?
HH(d,a,b,c,M0,11,0xeaa127fa)?
HH(c,d,a,b,M3,16,0xd4ef3085)?
HH(b,c,d,a,M6,23,0×04881d05)?
HH(a,b,c,d,M9,4,0xd9d4d039)?
HH(d,a,b,c,M12,11,0xe6db99e5)?
HH(c,d,a,b,M15,16,0×1fa27cf8)?
HH(b,c,d,a,M2,23,0xc4ac5665)?
?
第四輪?
II(a,b,c,d,M0,6,0xf4292244)?
II(d,a,b,c,M7,10,0×432aff97)?
II(c,d,a,b,M14,15,0xab9423a7)?
II(b,c,d,a,M5,21,0xfc93a039)?
II(a,b,c,d,M12,6,0×655b59c3)?
II(d,a,b,c,M3,10,0×8f0ccc92)?
II(c,d,a,b,M10,15,0xffeff47d)?
II(b,c,d,a,M1,21,0×85845dd1)?
II(a,b,c,d,M8,6,0×6fa87e4f)?
II(d,a,b,c,M15,10,0xfe2ce6e0)?
II(c,d,a,b,M6,15,0xa3014314)?
II(b,c,d,a,M13,21,0×4e0811a1)?
II(a,b,c,d,M4,6,0xf7537e82)?
II(d,a,b,c,M11,10,0xbd3af235)?
II(c,d,a,b,M2,15,0×2ad7d2bb)?
II(b,c,d,a,M9,21,0xeb86d391)?
?
消息分以512位為一分組進行處理,每一個分組進行上述4輪共64次計算后,將A,B,C,D分別加上計算得到的a,b,c,d。當做新的A,B,C,D,并將這4個變量賦值給a,b,c,d再進行下一分組的運算。由于填充后的消息長度為(N+1)*512,則共需計算N+1個分組。計算所有數據分組后,這4個變量為最后的結果,即MD5值。
?
MD5算法實現:
?
實現一:
MD5.h
#ifndef _MD5H_ #define _MD5H_ #include <math.h> #include <Windows.h> #include <iostream> #include <string.h> #include <stdlib.h>void ROL(unsigned int &s, unsigned short cx); //32位數循環左移實現函數 void ltob(unsigned int &i); //B\L互轉,接受UINT類型 unsigned int* MD5(const char* mStr); //MD5加密函數,并執行數據填充 unsigned int* MD5_2(const char* mStr); //MD5加密函數,并執行數據填充,更優#endif?MD5.cpp
#include "MD5.h"//4組計算函數 inline unsigned int F(unsigned int X, unsigned int Y, unsigned int Z) {return (X & Y) | ((~X) & Z); } inline unsigned int G(unsigned int X, unsigned int Y, unsigned int Z) {return (X & Z) | (Y & (~Z)); } inline unsigned int H(unsigned int X, unsigned int Y, unsigned int Z) {return X ^ Y ^ Z; } inline unsigned int I(unsigned int X, unsigned int Y, unsigned int Z) {return Y ^ (X | (~Z)); }//32位數循環左移(或稱右移)實現函數 void ROL(unsigned int &s, unsigned short cx) {if (cx > 32)cx %= 32;s = (s << cx) | (s >> (32 - cx));return; }//B\L互轉,接收UINT類型 void ltob(unsigned int &i) {unsigned int tmp = i;//保存副本byte *psour = (byte*)&tmp, *pdes = (byte*)&i;pdes += 3;//調整指針,準備左右調轉for (short j = 3; j >= 0; --j){CopyMemory(pdes - j, psour + j, 1);}return; }//MD5循環計算函數,label=第幾輪循環(1<=label<=4),lGroup數組=4個種子副本,M=數據(16組32位數指針) void AccLoop(unsigned short label, unsigned int *lGroup, void *M) {unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定義:4個指針; T表累加器; 局部變量typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定義函數類型//循環左移-位數表const unsigned int rolarray[4][4] = {{ 7, 12, 17, 22 },{ 5, 9, 14, 20 },{ 4, 11, 16, 23 },{ 6, 10, 15, 21 }};//數據坐標表const unsigned short mN[4][16] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }};const unsigned int *pM = static_cast<unsigned int*>(M);//轉換類型為32位的UintTAcc = ((label - 1) * 16) + 1;clac clacArr[4] = { F, G, H, I }; //定義并初始化計算函數指針數組//一輪循環開始(16組->16次)for (short i = 0; i < 16; ++i){/*進行指針自變換*/i1 = lGroup + ((0 + i) % 4);i2 = lGroup + ((1 + i) % 4);i3 = lGroup + ((2 + i) % 4);i4 = lGroup + ((3 + i) % 4);if(0 == i%2){//計算開始: A+F(B,C,D)+M[i]+T[i+1]tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));//循環左移ROL(tmpi, rolarray[label - 1][i % 4]);//相加并賦值到下一個種子*i1 = *i2 + tmpi;}else{tmpi = (*i3 + clacArr[label - 1](*i4, *i1, *i2) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));//循環左移ROL(tmpi, rolarray[label - 1][i % 4]);//相加并賦值到下一個種子*i3 = *i4 + tmpi;}}return; }//加密函數 unsigned int* MD5(const char* mStr) {//計算緩沖區長度,并進行數據填充unsigned int mLen = strlen(mStr);if (mLen <= 0) {return 0;}unsigned int FillSize = 448 - ((mLen * 8) % 512); //計算需填充的bit數unsigned int FSbyte = FillSize / 8; //以字節表示的填充數//預留512-448=64bit,填充原消息的長度unsigned int BuffLen = mLen + 8 + FSbyte; //緩沖區長度unsigned char *md5Buff = new unsigned char[BuffLen]; //分配緩沖區CopyMemory(md5Buff, mStr, mLen); //復制字符串到緩沖區//數據填充md5Buff[mLen] = 0x80; //第一個bit填充1ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0unsigned long long lenBit = mLen * 8ULL; //計算字符串長度,準備填充后64bitCopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8);//數據運算unsigned int LoopNumber = BuffLen / 64; //以16個字為一分組,計算分組數量unsigned int A = 0x67452301, B = 0xEFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4個種子,小端類型unsigned int *lGroup = new unsigned int[4];lGroup[0] = A;lGroup[1] = B;lGroup[2] = C;lGroup[3] = D;for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分組大循環開始{//進入4次計算的小循環,共4*16次for (unsigned short Lcount = 0; Lcount < 4;){AccLoop(++Lcount, lGroup, &md5Buff[Bcount * 64]);}//數據相加作為下一輪的種子或者最終輸出A = (lGroup[0] += A);B = (lGroup[1] += B);C = (lGroup[2] += C);D = (lGroup[3] += D);}//轉換內存中的布局后才能正常顯示ltob(lGroup[0]);ltob(lGroup[1]);ltob(lGroup[2]);ltob(lGroup[3]);delete[] md5Buff;return lGroup; }/* MD5循環計算函數,label=第幾輪循環(1<=label<=4),lGroup數組=4個種子副本,M=數據(16組32位數指針) 種子數組排列方式: --A--D--C--B--,即 lGroup[0]=A; lGroup[1]=D; lGroup[2]=C; lGroup[3]=B; */ void AccLoop_2(unsigned short label, unsigned int *lGroup, void *M) {unsigned int *i1, *i2, *i3, *i4, TAcc, tmpi = 0; //定義:4個指針; T表累加器; 局部變量typedef unsigned int(*clac)(unsigned int X, unsigned int Y, unsigned int Z); //定義函數類型const unsigned int rolarray[4][4] = {{ 7, 12, 17, 22 },{ 5, 9, 14, 20 },{ 4, 11, 16, 23 },{ 6, 10, 15, 21 }};//循環左移-位數表const unsigned short mN[4][16] = {{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },{ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 },{ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 },{ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }};//數據坐標表const unsigned int *pM = static_cast<unsigned int*>(M);//轉換類型為32位的UintTAcc = ((label - 1) * 16) + 1; //根據第幾輪循環初始化T表累加器clac clacArr[4] = { F, G, H, I }; //定義并初始化計算函數指針數組/*一輪循環開始(16組->16次)*/for (short i = 0; i < 16; ++i){/*進行指針自變換*/i1 = lGroup + ((0 + i) % 4);i2 = lGroup + ((3 + i) % 4);i3 = lGroup + ((2 + i) % 4);i4 = lGroup + ((1 + i) % 4);/*第一步計算開始: A+F(B,C,D)+M[i]+T[i+1] 注:第一步中直接計算T表*/tmpi = (*i1 + clacArr[label - 1](*i2, *i3, *i4) + pM[(mN[label - 1][i])] + (unsigned int)(0x100000000UL * abs(sin((double)(TAcc + i)))));ROL(tmpi, rolarray[label - 1][i % 4]);//第二步:循環左移*i1 = *i2 + tmpi;//第三步:相加并賦值到種子}return; }/*接口函數,并執行數據填充*/ unsigned int* MD5_2(const char* mStr) {unsigned int mLen = strlen(mStr); //計算字符串長度if (mLen < 0) return 0;unsigned int FillSize = 448 - ((mLen * 8) % 512); //計算需填充的bit數unsigned int FSbyte = FillSize / 8; //以字節表示的填充數unsigned int BuffLen = mLen + 8 + FSbyte; //緩沖區長度或者說填充后的長度unsigned char *md5Buff = new unsigned char[BuffLen]; //分配緩沖區CopyMemory(md5Buff, mStr, mLen); //復制字符串到緩沖區/*數據填充開始*/md5Buff[mLen] = 0x80; //第一個bit填充1ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另一可用函數為FillMemoryunsigned long long lenBit = mLen * 8ULL; //計算字符串長度,準備填充CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充長度/*數據填充結束*//*運算開始*/unsigned int LoopNumber = BuffLen / 64; //以16個字為一分組,計算分組數量unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4個種子,小端類型//unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //種子副本數組,并作為返回值返回unsigned int *lGroup = new unsigned int[4];lGroup[0] = A;lGroup[1] = D;lGroup[2] = C;lGroup[3] = B;for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分組大循環開始{/*進入4次計算的小循環*/for (unsigned short Lcount = 0; Lcount < 4;){AccLoop_2(++Lcount, lGroup, &md5Buff[Bcount * 64]);}/*數據相加作為下一輪的種子或者最終輸出*/A = (lGroup[0] += A);B = (lGroup[3] += B);C = (lGroup[2] += C);D = (lGroup[1] += D);}/*轉換內存中的布局后才能正常顯示*/ltob(lGroup[0]);ltob(lGroup[1]);ltob(lGroup[2]);ltob(lGroup[3]);delete[] md5Buff; //清除內存并返回return lGroup; }?
main.cpp
#include "MD5.h"int main() {char tmpstr[256], buf[4][10];std::cout << "請輸入要加密的字符串:";std::cin >> tmpstr;//char buf[4][10];//char tmpstr[] ="admin";//MD5值:21232F297A57A5A743894A0E4A801FC3unsigned int* tmpGroup = MD5(tmpstr);sprintf_s(buf[0], "%8X", tmpGroup[0]); //Asprintf_s(buf[1], "%8X", tmpGroup[1]); //Bsprintf_s(buf[2], "%8X", tmpGroup[2]); //Csprintf_s(buf[3], "%8X", tmpGroup[3]); //Dstd::cout <<"1-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;tmpGroup = MD5_2(tmpstr);sprintf_s(buf[0], "%8X", tmpGroup[0]); //Asprintf_s(buf[1], "%8X", tmpGroup[3]); //Bsprintf_s(buf[2], "%8X", tmpGroup[2]); //Csprintf_s(buf[3], "%8X", tmpGroup[1]); //Dstd::cout <<"2-MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl;delete[] tmpGroup;return 0; }實現二:
#include <string.h> #include <math.h> #include <stdio.h> #include <iostream> #include <string.h> #include <stdlib.h>/*********************************** * 非線性函數 * (&是與,|是或,~是非,^是異或) * * 這些函數是這樣設計的: * 如果X、Y和Z的對應位是獨立和均勻的, * 那么結果的每一位也應是獨立和均勻的。 * * 函數F是按逐位方式操作:如果X,那么Y,否則Z。 * 函數H是逐位奇偶操作符 **********************************/ #define F(x,y,z) ((x & y) | (~x & z)) #define G(x,y,z) ((x & z) | (y & ~z)) #define H(x,y,z) (x^y^z) #define I(x,y,z) (y ^ (x | ~z)) /************************************** *向左換移(右環移)n個單位 * ************************************/ #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n))) /**************************************************** * 每次操作對a,b,c和d中的其中三個作一次非線性函數運算 * F(b,c,d) G(b,c,d) H(b,c,d) I(b,c,d) * * 然后將所得結果加上 第四個變量(a), * F(b,c,d)+a * * 文本的一個子分組(x) * F(b,c,d)+a+x * * 和一個常數(ac)。 * F(b,c,d)+a+x+ac * * 再將所得結果向右環移一個不定的數(s), * ROTATE_LEFT( F(b,c,d)+a+x+ac , s ) * * 并加上a,b,c或d中之一(b)。 * ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b * * 最后用該結果取代a,b,c或d中之一(a)。 * a=ROTATE_LEFT( F(b,c,d)+a+x+ac , s )+b * * ***************************************************/ #define FF(a,b,c,d,x,s,ac) { a += F(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; } #define GG(a,b,c,d,x,s,ac) { a += G(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; } #define HH(a,b,c,d,x,s,ac) { a += H(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; } #define II(a,b,c,d,x,s,ac) { a += I(b,c,d) + x + ac; a = ROTATE_LEFT(a,s); a += b; }//儲存一個MD5 text信息 typedef struct { unsigned int count[2]; //記錄當前狀態,其數據位數 unsigned int state[4]; //4個數,一共32位 記錄用于保存對512bits信息加密的中間結果或者最終結果 unsigned char buffer[64];//一共64字節,512位 }MD5_CTX; //第一位1 其后若干個0,用于MD5Final函數時的補足 unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //函數聲明 void MD5Init(MD5_CTX *context); void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen); void MD5Final(MD5_CTX *context,unsigned char digest[16]); void MD5Transform(unsigned int state[4],unsigned char block[64]); void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len); void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len); /************************ * 函數功能:初始化一個MD5 text * 函數參數:MD5 text 指針 * ***********************/ //初始化 void MD5Init(MD5_CTX *context) { context->count[0] = 0; context->count[1] = 0; //分別賦固定值 context->state[0] = 0x67452301;context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; } /************************************************ * 函數功能:對一個MD5 text,把輸入的數據進行分組,并進行加密 * 未用到的數據把其儲存在MD5 text中。 * * 參數分析: * MD5_CTX *context :一個MD5 text * unsigned char *input :新添加的數據 * unsigned int inputlen :新添加數據的長度(字節) * ***********************************************/void MD5Update(MD5_CTX *context, unsigned char *input, unsigned int inputlen) { unsigned int i = 0,index = 0,partlen = 0; //index:當前狀態的位數對64取余,其單位是字節//也可以寫作: index=(context->count[0]/8)%64index = (context->count[0] >> 3) & 0x3F; //partlen:可以補齊64字節的字節數partlen = 64 - index;//下面代碼是解決一個unsignde int 無法儲存極大數據導致溢出的問題//當前位數加上新添加的位數,由于inputlen是以字節為單位,所以其轉換為位數//相當于context->count[0] += inputlen*8; context->count[0] += inputlen << 3; //當其出現溢出的情況時,通過以下操作把兩個16位的數連在一塊,生成一個//32位的二進制數串,從而擴大其儲存范圍if(context->count[0] < (inputlen << 3)){context->count[1]++;}//該語句可替換為 context->count[1]+=(inputlen<<3)>>32;//便于理解context->count[1] += inputlen >> 29; //當其輸入字節數的大于其可以補足64字節的字節數,進行補足if(inputlen >= partlen) { //向buffer中補足partlen個字節,使其到達64字節memcpy(&context->buffer[index], input, partlen);//buffer達到64字節512位,則把其作為一組進行運算MD5Transform(context->state, context->buffer); //如果輸入的數據還可以組成多個64字節,則把其可以組成//的作為若干組進行運算for(i = partlen;i+64 <= inputlen;i+=64) MD5Transform(context->state, &input[i]); //恢復0值,照應 下面 把輸入 剩余字節(不能組成64字節組) 儲存的操作index = 0; } //否則,把輸入的數據按順序放在原來數據后面else { i = 0; } //放置剩余數據memcpy(&context->buffer[index],&input[i],inputlen-i); } /************************************************* * 函數功能:對數據進行補足,并加入數據位數信息,并進一步加密 * * 參數分析: * MD5_CTX *context :一個MD5 text * unsigned char digest[16] :儲存加密結果的數組 *************************************************/void MD5Final(MD5_CTX *context,unsigned char digest[16]) { unsigned int index = 0,padlen = 0; //bits: 8個字節,64位unsigned char bits[8]; //index:對64取余結果index = (context->count[0] >> 3) & 0x3F; //因為要填充滿足使其位長對512求余的結果等于448(56位)//所以當其所剩余的數小于56字節,則填充56-index字節,//否則填充120-index字節//這里padlen代表其所需填充的字節padlen = (index < 56)?(56-index):(120-index); //然后,在這個結果后面附加一個以64位二進制表示的填充前數據長度。//把填充前數據數據長度轉換后放到bit字符數組中MD5Encode(bits,context->count,8);//根據已經存儲好的數組PADDING,在信息的后面填充一個1和無數個0,//直到滿足上面的條件時才停止用0對信息的填充//其填充后進行了一系列的加密操作,其定剩余48個字節MD5Update(context,PADDING,padlen); //在最后添加進8個字節的數據長度信息,最后湊成一組,進行一次加密處理MD5Update(context,bits,8); //把最終得到的加密信息變成字符輸出,共16字節MD5Encode(digest,context->state,16); } /********************************************************** * 函數功能:利用位操作,按1->4方式把數字分解成字符 * * 參數分析: * unsigned char *output :輸出的字符的數組 * unsigned int *input :輸入數字的數組 * unsigned int len : 輸入數字數組的長度(單位:位) * *********************************************************/void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len) { unsigned int i = 0,j = 0; while(j < len) { //這里& 0xFF為取后8位//i代表數字數組下標//j代表字符數組下標//把數字的8、8-16、16-24、24-32分別賦值給字符output[j] = input[i] & 0xFF; output[j+1] = (input[i] >> 8) & 0xFF; output[j+2] = (input[i] >> 16) & 0xFF; output[j+3] = (input[i] >> 24) & 0xFF; i++; j+=4; } } /********************************************************** * 函數功能:利用位操作,按4->1方式把字符合成數字 * * 參數分析: * unsigned int *output :輸出的數字的數組 * unsigned char *input :輸入字符的數組 * unsigned int len : 輸入字符的長度 (單位:位) * *********************************************************/void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len) { unsigned int i = 0,j = 0; while(j < len) { //利用位操作,把四個單位為1字節的字符,合成一個單位為4字節的數字//因為FF GG HH II和非線性函數都只能對數字進行處理//第一個字符占前8位,第二個占8-16位,第三個占16-24位,第四個占//24-32位。//i代表數字數組下標//j代表字符數組下標output[i] = (input[j]) | (input[j+1] << 8) | (input[j+2] << 16) | (input[j+3] << 24); i++; j+=4; } } /************************************************************** * 函數功能:對512位的block數據進行加密,并把加密結果存入state數組中 * 對512位信息(即block字符數組)進行一次處理,每次處理包括四輪 *state[4]:md5結構中的state[4],用于保存對512bits信息加密的中間結果或者最終結果 * block[64]:欲加密的512bits信息或其中間數據 ***************************************************************/ void MD5Transform(unsigned int state[4], unsigned char block[64]) { //a b c d繼承上一個加密的結果,所以其具有繼承性unsigned int a = state[0]; unsigned int b = state[1]; unsigned int c = state[2]; unsigned int d = state[3]; //這里只需用到16個,我把原來的unsiged int x[64] 改為了 x[16]unsigned int x[16]; //把字符轉化成數字,便于運算MD5Decode(x,block,64); //具體函數方式固定,不再贅述/*************第一輪******************/FF(a, b, c, d, x[ 0], 7, 0xd76aa478); FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); FF(c, d, a, b, x[ 2], 17, 0x242070db); FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); FF(d, a, b, c, x[ 5], 12, 0x4787c62a); FF(c, d, a, b, x[ 6], 17, 0xa8304613); FF(b, c, d, a, x[ 7], 22, 0xfd469501); FF(a, b, c, d, x[ 8], 7, 0x698098d8); FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); FF(c, d, a, b, x[10], 17, 0xffff5bb1); FF(b, c, d, a, x[11], 22, 0x895cd7be); FF(a, b, c, d, x[12], 7, 0x6b901122); FF(d, a, b, c, x[13], 12, 0xfd987193); FF(c, d, a, b, x[14], 17, 0xa679438e); FF(b, c, d, a, x[15], 22, 0x49b40821); /*************第二輪*****************/GG(a, b, c, d, x[ 1], 5, 0xf61e2562); GG(d, a, b, c, x[ 6], 9, 0xc040b340); GG(c, d, a, b, x[11], 14, 0x265e5a51); GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); GG(a, b, c, d, x[ 5], 5, 0xd62f105d); GG(d, a, b, c, x[10], 9, 0x2441453); GG(c, d, a, b, x[15], 14, 0xd8a1e681); GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); GG(d, a, b, c, x[14], 9, 0xc33707d6); GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); GG(b, c, d, a, x[ 8], 20, 0x455a14ed); GG(a, b, c, d, x[13], 5, 0xa9e3e905); GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); GG(c, d, a, b, x[ 7], 14, 0x676f02d9); GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /*************第三輪*****************/HH(a, b, c, d, x[ 5], 4, 0xfffa3942); HH(d, a, b, c, x[ 8], 11, 0x8771f681); HH(c, d, a, b, x[11], 16, 0x6d9d6122); HH(b, c, d, a, x[14], 23, 0xfde5380c); HH(a, b, c, d, x[ 1], 4, 0xa4beea44); HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); HH(b, c, d, a, x[10], 23, 0xbebfbc70); HH(a, b, c, d, x[13], 4, 0x289b7ec6); HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); HH(b, c, d, a, x[ 6], 23, 0x4881d05); HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); HH(d, a, b, c, x[12], 11, 0xe6db99e5); HH(c, d, a, b, x[15], 16, 0x1fa27cf8); HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /*************第四輪******************/II(a, b, c, d, x[ 0], 6, 0xf4292244); II(d, a, b, c, x[ 7], 10, 0x432aff97); II(c, d, a, b, x[14], 15, 0xab9423a7); II(b, c, d, a, x[ 5], 21, 0xfc93a039); II(a, b, c, d, x[12], 6, 0x655b59c3); II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); II(c, d, a, b, x[10], 15, 0xffeff47d); II(b, c, d, a, x[ 1], 21, 0x85845dd1); II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); II(d, a, b, c, x[15], 10, 0xfe2ce6e0); II(c, d, a, b, x[ 6], 15, 0xa3014314); II(b, c, d, a, x[13], 21, 0x4e0811a1); II(a, b, c, d, x[ 4], 6, 0xf7537e82); II(d, a, b, c, x[11], 10, 0xbd3af235); II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); II(b, c, d, a, x[ 9], 21, 0xeb86d391); //更換原來的結果state[0] += a; state[1] += b; state[2] += c; state[3] += d; }int main(int argc, char *argv[]) { MD5_CTX md5; //定義一個MD5 textMD5Init(&md5);//初始化int i;//unsigned char encrypt[] ="admin";//要加密內容//加密結果:21232f297a57a5a743894a0e4a801fc3unsigned char encrypt[1000];//要加密內容printf("請輸入要加密的字符串:");gets((char *)encrypt);unsigned char decrypt[16]; //加密結果MD5Update(&md5, encrypt, strlen((char *)encrypt));//進行初步分組加密MD5Final(&md5,decrypt); //進行后序的補足,并加密 printf("加密前:%s\n加密后16位:",encrypt); for(i=4;i<12;i++) { printf("%02x",decrypt[i]);} printf("\n加密前:%s\n加密后32位:",encrypt); for(i=0;i<16;i++) { printf("%02x",decrypt[i]);}printf("\n");return 0; }?
?
總結
以上是生活随笔為你收集整理的MD5加密概述,原理及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在IT的路上,我在成长
- 下一篇: IE8 兼容background-siz