1.什麼是MD5
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整一致。是計算機普遍使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言廣泛已有MD5實現。將數據(如漢字)運算爲另外一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD二、MD3和MD4。——百度百科ios
MD5其實不算是加密算法,而是一種信息的摘要。它的特性是不可逆的,因此除了暴力破解 通常逆序算法是得不到結果的。例如:使用以下算法進行加密,對一個字符串利用它的長度和每一位的ASCII相加得出值,好比「bc」那麼就是2+98+99=199,若是利用這個算法進行逆推的得出的答案就不惟一,好比"ac",「Ho」,"AAB"等等。算法
2.MD5加鹽
若是直接對密碼進行散列,那麼黑客能夠對經過得到這個密碼散列值,而後經過查散列值字典(例如MD5密碼破解網站),獲得某用戶的密碼。編程
加Salt能夠必定程度上解決這一問題。所謂加Salt方法,就是加點「佐料」。其基本想法是這樣的:當用戶首次提供密碼時(一般是註冊時),由系統自動往這個密碼裏撒一些「佐料」,而後再散列。而當用戶登陸時,系統爲用戶提供的代碼撒上一樣的「佐料」,而後散列,再比較散列值,已肯定密碼是否正確。數組
3.MD5的加密步驟
接下來大體羅列一下MD5加密的計算步驟,能夠參考這個視頻,MD5算法視頻編程語言
一、數據填充
對消息進行數據填充,使消息的長度對512取模得448,設消息長度爲X,即知足X mod 512=448。根據此公式得出須要填充的數據長度。
填充方法:在消息後面進行填充,填充第一位爲1,其他爲0。函數
二、添加消息長度
在第一步結果以後再填充上原消息的長度,可用來進行存儲的長度爲64位。
在此步驟進行完畢後,最終消息長度就是512的整數倍。oop
三、數據處理
準備須要用到的數據:
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++的實現:佈局
#ifndef MD5H #define MD5H #include <math.h> #include <Windows.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時調用此函數 #endif
MD5.cpp網站
/*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)); } /*4組計算函數結束*/ /*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 i = 3; i >= 0; --i) { CopyMemory(pdes - i, psour + i, 1); } return; } /* 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(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位的Uint TAcc = ((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(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填充1 ZeroMemory(&md5Buff[mLen + 1], FSbyte - 1); //其它bit填充0,另外一可用函數爲FillMemory unsigned long long lenBit = mLen * 8ULL; //計算字符串長度,準備填充 CopyMemory(&md5Buff[mLen + FSbyte], &lenBit, 8); //填充長度 /*數據填充結束*/ /*運算開始*/ unsigned int LoopNumber = BuffLen / 64; //以64個字爲一分組,計算分組數量 unsigned int A = 0x67452301, B = 0x0EFCDAB89, C = 0x98BADCFE, D = 0x10325476;//初始4個種子,小端類型 unsigned int *lGroup = new unsigned int[4]{ A, D, C, B}; //種子副本數組,並做爲返回值返回 for (unsigned int Bcount = 0; Bcount < LoopNumber; ++Bcount) //分組大循環開始 { /*進入4次計算的小循環*/ for (unsigned short Lcount = 0; Lcount < 4;) { AccLoop(++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 <iostream> #include <string.h> #include <stdlib.h> #include "MD5.h" int main(int argc, char **argv) { char tmpstr[256], buf[4][10]; std::cout << "請輸入要加密的字符串:"; std::cin >> tmpstr; unsigned int* tmpGroup = MD5(tmpstr); sprintf_s(buf[0], "%8X", tmpGroup[0]); sprintf_s(buf[1], "%8X", tmpGroup[3]); sprintf_s(buf[2], "%8X", tmpGroup[2]); sprintf_s(buf[3], "%8X", tmpGroup[1]); std::cout <<"MD5:"<< buf[0] << buf[1] << buf[2] << buf[3] << std::endl; delete[] tmpGroup; return 0; //在此下斷點才能看到輸出的值 }