MD5

計算機廣泛使用的散列演演算法之一

MD5即Message-Digest Algorithm5(信息摘要演演算法5),是計算機廣泛使用的散列演演算法之一(又譯摘要演演算法、哈希演演算法)。經MD2、MD3和MD4發展而來,誕生於20世紀90年代初。用於確保信息傳輸完整一致。雖然已被破解,但仍然具有較好的安全性,加之可以免費使用,所以仍廣泛運用於數字簽名、文件完整性驗證以及口令加密等領域。

MD5即Message-Digest Algorithm5(信息-摘要演演算法5),用於確保信息傳輸完整一致。是計算機廣泛使用的雜湊演演算法之一(又譯摘要演演算法、哈希演演算法),主流編程語言普遍已有MD5實現。將數據(如漢字)運算為另一固定長度值,是雜湊演演算法的基礎原理,MD5的前身有MD2、MD3和MD4。

基本簡介


MD5
MD5
MD5的全稱是Message-digest Algorithm5(中文譯作信息摘要演演算法5),用於確保信息傳輸完整一致。是計算機廣泛使用的散列演演算法之一。它經MD2、MD3和MD4發展而來,在20世紀90年代初誕生。
它的作用是讓大容量信息在用數字簽名軟體簽署私人密鑰前被“壓縮”成一種保密的格式(就是把一個任意長度的位元組串變換成一定長的大整數)。不管是MD2、MD4還是MD5,它們都需要獲得一個隨機長度的信息併產生一個128位的信息摘要。雖然這些演演算法的結構或多或少有些相似,但MD2的設計與MD4和MD5完全不同,那是因為MD2是為8位機器做過設計優化的,而MD4和MD5卻是面向32位的電腦。

發展歷史


MD5
MD5
1992年8月,羅納德·李維斯特向網際網路工程任務組(IETF)提交了一份重要文件,描述了這種演演算法的原理。由於這種演演算法的公開性和安全性,在90年代被廣泛使用在各種程序語言中,用以確保資料傳遞無誤等。
MD5由MD4、MD3、MD2改進而來,主要增強演演算法複雜度和不可逆性。MD5演演算法因其普遍、穩定、快速的特點,仍廣泛應用於普通數據的加密保護領域。
MD2
Rivest在1989年開發出MD2演演算法。在這個演演算法中,首先對信息進行數據補位,使信息的位元組長度是16的倍數。然後,以一個16位的校驗和追加到信息末尾,並且根據這個新產生的信息計算出散列值。後來,Rogier和Chauvaud發現如果忽略了校驗和MD2將產生衝突。MD2演演算法加密后結果是唯一的(即不同信息加密后的結果不同)。
MD4
為了加強演演算法的安全性,Rivest在1990年又開發出MD4演演算法。MD4演演算法同樣需要填補信息以確保信息的比特位長度減去448后能被512整除(信息比特位長度mod512=448)。然後,一個以64位二進位表示的信息的最初長度被添加進來。信息被處理成512位damgard/merkle迭代結構的區塊,而且每個區塊要通過三個不同步驟的處理。Denboer和Bosselaers以及其他人很快的發現了攻擊MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的個人電腦在幾分鐘內找到MD4完整版本中的衝突(這個衝突實際上是一種漏洞,它將導致對不同的內容進行加密卻可能得到相同的加密后結果)。
MD5
1991年,Rivest開發出技術上更為趨近成熟的MD5演演算法。它在MD4的基礎上增加了"安全帶"(safety-belts)的概念。雖然MD5比MD4複雜度大一些,但卻更為安全。這個演演算法很明顯的由四個和MD4設計有少許不同的步驟組成。在MD5演演算法中,信息-摘要的大小和填充的必要條件與MD4完全相同。Den boer和Bosselaers曾發現MD5演演算法中的假衝突(pseudo-collisions),但除此之外就沒有其他被發現的加密后結果了。

研究


MD5
MD5
2004年8月17日的美國加州聖巴巴拉的國際密碼學會議(Crypto2004)上,來自中國山東大學的王小雲教授做了破譯MD5、HAVAL-128、MD4和RIPEMD演演算法的報告,公布了MD系列演演算法的破解結果。宣告了世界通行密碼標準MD5不再安全。
2008年,荷蘭埃因霍芬技術大學科學家成功把2個可執行文件進行了MD5碰撞,使得這兩個運行結果不同的程序被計算出同一個MD。2008年12月一組科研人員通過MD5碰撞成功生成了偽造的SSL證書,這使得在https協議中伺服器可以偽造一些根CA的簽名。

安全性分析


MD5相對MD4所作的改進:
● ● 增加了第四輪。
● ● 每一步均有唯一的加法常數。
● ● 減弱第二輪中函數的對稱性。
● ● 第一步加上了上一步的結果,這將引起更快的雪崩效應(就是對明文或者密鑰改變1bit都會引起密文的巨大不同)。
● ● 改變了第二輪和第三輪中訪問消息子分組的次序,使其更不相似。
● ● 近似優化了每一輪中的循環左移位移量以實現更快的雪崩效應,各輪的位移量互不相同。
MD5演演算法自誕生之日起,就有很多人試圖證明和發現它的不安全之處,即存在碰撞(在對兩個不同的內容使用MD5演演算法運算的時候,有可能得到一對相同的結果值)。2009年,中國科學院的謝濤和馮登國僅用了的碰撞演演算法複雜度,破解了MD5的碰撞抵抗,該攻擊在普通計算機上運行只需要數秒鐘。

破解方法


對信息系統或者網站系統來說,MD5演演算法主要用在用戶註冊口令的加密,對於普通強度的口令加密,可以通過以下三種方式進行破解:
(1)在線查詢密碼。一些在線的MD5值查詢網站提供MD5密碼值的查詢,輸入MD5密碼值后,如果在資料庫中存在,那麼可以很快獲取其密碼值。
(2)使用MD5破解工具。網路上有許多針對MD5破解的專用軟體,通過設置字典來進行破解。
(3)通過社會工程學來獲取或者重新設置用戶的口令。
因此簡單的MD5加密是沒有辦法達到絕對的安全的,因為普通的MD5加密有多種暴力破解方式,因此如果想要保證信息系統或者網站的安全,需要對MD5進行改造,增強其安全性。但對於公司以及普通用戶來說,從演演算法上來破解MD5非常困難,因此MD5仍然算是一種安全的演演算法。

代碼


C++實現
#include
#include
usingnamespacestd;
#defineshift(x,n)(((x)<<(n))|((x)>>(32-(n))))//右移的時候,高位一定要補零,而不是補充符號位
#defineF(x,y,z)(((x)&(y))|((~x)&(z)))
#defineG(x,y,z)(((x)&(z))|((y)&(~z)))
#defineH(x,y,z)((x)^(y)^(z))
#defineI(x,y,z)((y)^((x)|(~z)))
#defineA0x67452301
#defineB0xefcdab89
#defineC0x98badcfe
#defineD0x10325476
//strBaye的長度
unsignedintstrlength;
//A,B,C,D的臨時變數
unsignedintatemp;
unsignedintbtemp;
unsignedintctemp;
unsignedintdtemp;
//常量tiunsignedint(abs(sin(i+1))*(2pow32))
constunsignedintk[]={
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
//向左位移數
constunsignedints[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
15,21,6,10,15,21,6,10,15,21,6,10,15,21};
constcharstr16[]="0123456789abcdef";
voidmainLoop(unsignedintM[])
{
unsignedintf,g;
unsignedinta=atemp;
unsignedintb=btemp;
unsignedintc=ctemp;
unsignedintd=dtemp;
for(unsignedinti=0;i<64;i++)
{
if(i<16){
f=F(b,c,d);
g=i;
}elseif(i<32)
{
f=G(b,c,d);
g=(5*i+1)%16;
}elseif(i<48){
f=H(b,c,d);
g=(3*i+5)%16;
}else{
f=I(b,c,d);
g=(7*i)%16;
}
unsignedinttmp=d;
d=c;
c=b;
b=b+shift((a+f+k[i]+M[g]),s[i]);
a=tmp;
}
atemp=a+atemp;
btemp=b+btemp;
ctemp=c+ctemp;
dtemp=d+dtemp;
}
unsignedint*add(stringstr)
{
unsignedintnum=((str.length()+8)/64)+1;//以512位,64個位元組為一組
unsignedint*strByte=newunsignedint[num*16];//64/4=16,所以有16個整數
strlength=num*16;
for(unsignedinti=0;i
strByte[i]=0;
for(unsignedinti=0;i
{
strByte[i>>2]|=(str[i])<<((i%4)*8);//一個整數存儲四個位元組,i>>2表示i/4一個unsignedint對應4個位元組,保存4個字元信息
}
strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1一個unsignedint保存4個字元信息,所以用128左移
strByte[num*16-2]=str.length()*8;
returnstrByte;
}
stringchangeHex(inta)
{
intb;
stringstr1;
stringstr="";
for(inti=0;i<4;i++)
{
str1="";
b=((a>>i*8)%(1<<8))&0xff;//逆序處理每個位元組
for(intj=0;j<2;j++)
{
str1.insert(0,1,str16[b%16]);
b=b/16;
}
str+=str1;
}
returnstr;
}
stringgetMD5(stringsource)
{
atemp=A;//初始化
btemp=B;
ctemp=C;
dtemp=D;
unsignedint*strByte=add(source);
for(unsignedinti=0;i
{
unsignedintnum[16];
for(unsignedintj=0;j<16;j++)
num[j]=strByte[i*16+j];
mainLoop(num);
}
returnchangeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));
}
unsignedintmain()
{
stringss;
//cin>>ss;
strings=getMD5("abc");
cout<
return0;
}
JAVA實現
publicclassMD5{
privatefinalintA=0x67452301;
privatefinalintB=0xefcdab89;
privatefinalintC=0x98badcfe;
privatefinalintD=0x10325476;
privateintAtemp,Btemp,Ctemp,Dtemp;
privatefinalintK[]={
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
privatefinalints[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
15,21,6,10,15,21,6,10,15,21,6,10,15,21};
privatevoidinit(){
Atemp=A;
Btemp=B;
Ctemp=C;
Dtemp=D;
}
privateintshift(inta,ints){
return(a<>>(32-s));//右移的時候,高位一定要補零,而不是補充符號位
}
privatevoidMainLoop(intM[]){
intF,g;
inta=Atemp;
intb=Btemp;
intc=Ctemp;
intd=Dtemp;
for(inti=0;i<64;i++){
if(i<16){
F=(b&c)|((~b)&d);
g=i;
}elseif(i<32){
F=(d&b)|((~d)&c);
g=(5*i+1)%16;
}elseif(i<48){
F=b^c^d;
g=(3*i+5)%16;
}else{
F=c^(b|(~d));
g=(7*i)%16;
}
inttmp=d;
d=c;
c=b;
b=b+shift(a+F+K[i]+M[g],s[i]);
a=tmp;
}
Atemp=a+Atemp;
Btemp=b+Btemp;
Ctemp=c+Ctemp;
Dtemp=d+Dtemp;
}
privateint[]add(Stringstr){
intnum=((str.length()+8)/64)+1;//以512位,64個位元組為一組
intstrByte[]=newint[num*16];//64/4=16,所以有16個整數
for(inti=0;i
strByte[i]=0;
}
inti;
for(i=0;i
strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個位元組,小端序
}
strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
strByte[num*16-2]=str.length()*8;
returnstrByte;
}
publicStringgetMD5(Stringsource){
init();
intstrByte[]=add(source);
for(inti=0;i
intnum[]=newint[16];
for(intj=0;j<16;j++){
num[j]=strByte[i*16+j];
}
MainLoop(num);
}
returnchangeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
}
privateStringchangeHex(inta){
Stringstr="";
for(inti=0;i<4;i++){
str+=String.format("%2s",Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace('','0');
}
returnstr;
}
privatestaticMD5instance;
publicstaticMD5getInstance(){
if(instance==null){
instance=newMD5();
}
returninstance;
}
privateMD5(){};
publicstaticvoidmain(String[]args){
Stringstr=MD5.getInstance().getMD5("");
System.out.println(str);
}
}
結果錯誤
VB2010實現
ImportsSystem
ImportsSystem.Security.Cryptography
ImportsSystem.Text
ModuleExample
'哈希輸入字元串並返回一個32字元的十六進位字元串哈希。
FunctionGetMd5Hash(ByValinputAsString)AsString
'創建新的一個MD5CryptoServiceProvider對象的實例。
Dimmd5HasherAsNewMD5CryptoServiceProvider()
'輸入的字元串轉換為位元組數組,並計算哈希。
DimdataAsByte()=md5Hasher.ComputeHash(Encoding.Default.GetBytes(input))
'創建一個新的StringBuilder收集的位元組,並創建一個字元串。
DimsBuilderAsNewStringBuilder()
'通過每個位元組的哈希數據和格式為十六進位字元串的每一個循環。
ForiAsInteger=0Todata.Length-1
sBuilder.Append(data(i).ToString("x2"))
Next
'返回十六進位字元串。
ReturnsBuilder.ToString()
EndFunction
'驗證對一個字元串的哈希值。
FunctionVerifyMd5Hash(ByValinputAsString,ByValhashAsString)AsBoolean
'哈希的輸入。
DimhashOfInputAsString=GetMd5Hash(input)
'創建StringComparer的哈希進行比較。
DimcomparerAsStringComparer=StringComparer.OrdinalIgnoreCase
Returncomparer.Compare(hashOfInput,hash)=0
EndFunction
SubMain()
DimsourceAsString="HelloWorld!"
DimhashAsString=GetMd5Hash(source)
Console.WriteLine($"進行MD5加密的字元串為:{source},加密的結果是:{hash}。")
Console.WriteLine("正在驗證哈希……")
IfVerifyMd5Hash(source,hash)Then
Console.WriteLine("哈希值是
相同的。")
Else
Console.WriteLine("哈希值是不相同的。")
EndIf
EndSub
EndModule
'此代碼示例產生下面的輸出:
'進行MD5加密的字元串為:HelloWorld!,加密的結果是:ed076287532e86365e841e92bfc50d8c。
'正在驗證哈希……
'哈希值是相同的。
JavaScript實現
JavaScript版本的實現代碼,可以用於瀏覽器中運行和計算文本字元串的MD5。
functionmd5(string){
functionmd5_RotateLeft(lValue,iShiftBits){
return(lValue<>>(32-iShiftBits));
}
functionmd5_AddUnsigned(lX,lY){
varlX4,lY4,lX8,lY8,lResult;
lX8=(lX&0x80000000);
lY8=(lY&0x80000000);
lX4=(lX&0x40000000);
lY4=(lY&0x40000000);
lResult=(lX&0x3FFFFFFF)+(lY&0x3FFFFFFF);
if(lX4&lY4){
return(lResult^0x80000000^lX8^lY8);
}
if(lX4|lY4){
if(lResult&0x40000000){
return(lResult^0xC0000000^lX8^lY8);
}else{
return(lResult^0x40000000^lX8^lY8);
}
}else{
return(lResult^lX8^lY8);
}
}
functionmd5_F(x,y,z){
return(x&y)|((~x)&z);
}
functionmd5_G(x,y,z){
return(x&z)|(y&(~z));
}
functionmd5_H(x,y,z){
return(x^y^z);
}
functionmd5_I(x,y,z){
return(y^(x|(~z)));
}
functionmd5_FF(a,b,c,d,x,s,ac){
a=md5_AddUnsigned(a,md5_AddUnsigned(md5_AddUnsigned(md5_F(b,c,d),x),ac));
returnmd5_AddUnsigned(md5_RotateLeft(a,s),b);
};
functionmd5_GG(a,b,c,d,x,s,ac){
a=md5_AddUnsigned(a,md5_AddUnsigned(md5_AddUnsigned(md5_G(b,c,d),x),ac));
returnmd5_AddUnsigned(md5_RotateLeft(a,s),b);
};
functionmd5_HH(a,b,c,d,x,s,ac){
a=md5_AddUnsigned(a,md5_AddUnsigned(md5_AddUnsigned(md5_H(b,c,d),x),ac));
returnmd5_AddUnsigned(md5_RotateLeft(a,s),b);
};
functionmd5_II(a,b,c,d,x,s,ac){
a=md5_AddUnsigned(a,md5_AddUnsigned(md5_AddUnsigned(md5_I(b,c,d),x),ac));
returnmd5_AddUnsigned(md5_RotateLeft(a,s),b);
};
functionmd5_ConvertToWordArray(string){
varlWordCount;
varlMessageLength=string.length;
varlNumberOfWords_temp1=lMessageLength+8;
varlNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1%64))/64;
varlNumberOfWords=(lNumberOfWords_temp2+1)*16;
varlWordArray=Array(lNumberOfWords-1);
varlBytePosition=0;
varlByteCount=0;
while(lByteCount
lWordCount=(lByteCount-(lByteCount%4))/4;
lBytePosition=(lByteCount%4)*8;
lWordArray[lWordCount]=(lWordArray[lWordCount]|(string.charCodeAt(lByteCount)<
lByteCount++;
}
lWordCount=(lByteCount-(lByteCount%4))/4;
lBytePosition=(lByteCount%4)*8;
lWordArray[lWordCount]=lWordArray[lWordCount]|(0x80<
lWordArray[lNumberOfWords-2]=lMessageLength<<3;
lWordArray[lNumberOfWords-1]=lMessageLength>>>29;
returnlWordArray;
};
functionmd5_WordToHex(lValue){
varWordToHexValue="",
WordToHexValue_temp="",
lByte,lCount;
for(lCount=0;lCount<=3;lCount++){
lByte=(lValue>>>(lCount*8))&255;
WordToHexValue_temp="0"+lByte.toString(16);
WordToHexValue=WordToHexValue+WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
}
returnWordToHexValue;
};
functionmd5_Utf8Encode(string){
string=string.replace(/\r\n/g,"\n");
varutftext="";
for(varn=0;n
varc=string.charCodeAt(n);
if(c<128){
utftext+=String.fromCharCode(c);
}elseif((c>127)&&(c<2048)){
utftext+=String.fromCharCode((c>>6)|192);
utftext+=String.fromCharCode((c&63)|128);
}else{
utftext+=String.fromCharCode((c>>12)|224);
utftext+=String.fromCharCode(((c>>6)&63)|128);
utftext+=String.fromCharCode((c&63)|128);
}
}
returnutftext;
};
varx=Array();
vark,AA,BB,CC,DD,a,b,c,d;
varS11=7,
S12=12,
S13=17,
S14=22;
varS21=5,
S22=9,
S23=14,
S24=20;
varS31=4,
S32=11,
S33=16,
S34=23;
varS41=6,
S42=10,
S43=15,
S44=21;
string=md5_Utf8Encode(string);
x=md5_ConvertToWordArray(string);
a=0x67452301;
b=0xEFCDAB89;
c=0x98BADCFE;
d=0x10325476;
for(k=0;k
AA=a;
BB=b;
CC=c;
DD=d;
a=md5_FF(a,b,c,d,x[k+0],S11,0xD76AA478);
d=md5_FF(d,a,b,c,x[k+1],S12,0xE8C7B756);
c=md5_FF(c,d,a,b,x[k+2],S13,0x242070DB);
b=md5_FF(b,c,d,a,x[k+3],S14,0xC1BDCEEE);
a=md5_FF(a,b,c,d,x[k+4],S11,0xF57C0FAF);
d=md5_FF(d,a,b,c,x[k+5],S12,0x4787C62A);
c=md5_FF(c,d,a,b,x[k+6],S13,0xA8304613);
b=md5_FF(b,c,d,a,x[k+7],S14,0xFD469501);
a=md5_FF(a,b,c,d,x[k+8],S11,0x698098D8);
d=md5_FF(d,a,b,c,x[k+9],S12,0x8B44F7AF);
c=md5_FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
b=md5_FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
a=md5_FF(a,b,c,d,x[k+12],S11,0x6B901122);
d=md5_FF(d,a,b,c,x[k+13],S12,0xFD987193);
c=md5_FF(c,d,a,b,x[k+14],S13,0xA679438E);
b=md5_FF(b,c,d,a,x[k+15],S14,0x49B40821);
a=md5_GG(a,b,c,d,x[k+1],S21,0xF61E2562);
d=md5_GG(d,a,b,c,x[k+6],S22,0xC040B340);
c=md5_GG(c,d,a,b,x[k+11],S23,0x265E5A51);
b=md5_GG(b,c,d,a,x[k+0],S24,0xE9B6C7AA);
a=md5_GG(a,b,c,d,x[k+5],S21,0xD62F105D);
d=md5_GG(d,a,b,c,x[k+10],S22,0x2441453);
c=md5_GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
b=md5_GG(b,c,d,a,x[k+4],S24,0xE7D3FBC8);
a=md5_GG(a,b,c,d,x[k+9],S21,0x21E1CDE6);
d=md5_GG(d,a,b,c,x[k+14],S22,0xC33707D6);
c=md5_GG(c,d,a,b,x[k+3],S23,0xF4D50D87);
b=md5_GG(b,c,d,a,x[k+8],S24,0x455A14ED);
a=md5_GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
d=md5_GG(d,a,b,c,x[k+2],S22,0xFCEFA3F8);
c=md5_GG(c,d,a,b,x[k+7],S23,0x676F02D9);
b=md5_GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
a=md5_HH(a,b,c,d,x[k+5],S31,0xFFFA3942);
d=md5_HH(d,a,b,c,x[k+8],S32,0x8771F681);
c=md5_HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
b=md5_HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
a=md5_HH(a,b,c,d,x[k+1],S31,0xA4BEEA44);
d=md5_HH(d,a,b,c,x[k+4],S32,0x4BDECFA9);
c=md5_HH(c,d,a,b,x[k+7],S33,0xF6BB4B60);
b=md5_HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
a=md5_HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
d=md5_HH(d,a,b,c,x[k+0],S32,0xEAA127FA);
c=md5_HH(c,d,a,b,x[k+3],S33,0xD4EF3085);
b=md5_HH(b,c,d,a,x[k+6],S34,0x4881D05);
a=md5_HH(a,b,c,d,x[k+9],S31,0xD9D4D039);
d=md5_HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
c=md5_HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
b=md5_HH(b,c,d,a,x[k+2],S34,0xC4AC5665);
a=md5_II(a,b,c,d,x[k+0],S41,0xF4292244);
d=md5_II(d,a,b,c,x[k+7],S42,0x432AFF97);
c=md5_II(c,d,a,b,x[k+14],S43,0xAB9423A7);
b=md5_II(b,c,d,a,x[k+5],S44,0xFC93A039);
a=md5_II(a,b,c,d,x[k+12],S41,0x655B59C3);
d=md5_II(d,a,b,c,x[k+3],S42,0x8F0CCC92);
c=md5_II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
b=md5_II(b,c,d,a,x[k+1],S44,0x85845DD1);
a=md5_II(a,b,c,d,x[k+8],S41,0x6FA87E4F);
d=md5_II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
c=md5_II(c,d,a,b,x[k+6],S43,0xA3014314);
b=md5_II(b,c,d,a,x[k+13],S44,0x4E0811A1);
a=md5_II(a,b,c,d,x[k+4],S41,0xF7537E82);
d=md5_II(d,a,b,c,x[k+11],S42,0xBD3AF235);
c=md5_II(c,d,a,b,x[k+2],S43,0x2AD7D2BB);
b=md5_II(b,c,d,a,x[k+9],S44,0xEB86D391);
a=md5_AddUnsigned(a,AA);
b=md5_AddUnsigned(b,BB);
c=md5_AddUnsigned(c,CC);
d=md5_AddUnsigned(d,DD);
}
return(md5_WordToHex(a)+md5_WordToHex(b)+md5_WordToHex(c)+md5_WordToHex(d)).toLowerCase();
}

加密原理


MD5
MD5
MD5以512位分組來處理輸入的信息,且每一分組又被劃分為16個32位子分組,經過了一系列的處理后,演演算法的輸出由四個32位分組組成,將這四個32位分組級聯后將生成一個128位散列值。
在MD5演演算法中,首先需要對信息進行填充,使其位元組長度對512求餘數的結果等於448。因此,信息的位元組長度(BitsLength)將被擴展至N*512+448,即N*64+56個位元組(Bytes),N為一個正整數。填充的方法如下,在信息的後面填充一個1和無數個0個,直到滿足上面的條件時才停止用0對信息的填充。然後再在這個結果後面附加一個以64位二進位表示的填充前的信息長度。經過這兩步的處理,現在的信息位元組長度=N*512+448+64=(N+1)*512,即長度恰好是512的整數倍數。這樣做的原因是為滿足後面處理中對信息長度的要求。MD5中有四個32位被稱作鏈接變數(ChainingVariable)的整數參數,他們分別為:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。當設置好這四個鏈接變數后,就開始進入演演算法的四輪循環運算,循環的次數是信息中512位信息分組的數目。
將上面四個鏈接變數複製到另外四個變數中:A到a,B到b,C到c,D到d。主循環有四輪(MD4隻有三輪),每輪循環都很相似。第一輪進行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函數運算,然後將所得結果加上第四個變數(文本中的一個子分組和一個常數)。
再將所得結果向右環移一個不定的數,並加上a、b、c或d中之一。最後用該結果取代a、b、c或d中之一。以一下是每次操作中用到的四個非線性函數(每輪一個)。
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))
其中,⊕是異或,∧是與,∨是或,是反符號。
如果X、Y和Z的對應位是獨立和均勻的,那麼結果的每一位也應是獨立和均勻的。F是一個逐位運算的函數。即,如果X,那麼Y,否則Z。函數H是逐位奇偶操作符。所有這些完成之後,將A,B,C,D分別加上a,b,c,d。然後用下一分組數據繼續運行演演算法,最後的輸出是A,B,C和D的級聯。最後得到的A,B,C,D就是輸出結果,A是低位,D為高位,DCBA組成128位輸出結果。

軟體應用


用於密碼管理
當我們需要保存某些密碼信息以用於身份確認時,如果直接將密碼信息以明碼方式保存在資料庫中,不使用任何保密措施,系統管理員就很容易能得到原來的密碼信息,這些信息一旦泄露,密碼也很容易被破譯。為了增加安全性,有必要對資料庫中需要保密的信息進行加密,這樣,即使有人得到了整個資料庫,如果沒有解密演演算法,也不能得到原來的密碼信息。MD5演演算法可以很好地解決這個問題,因為它可以將任意長度的輸入串經過計算得到固定長度的輸出,而且只有在明文相同的情況下,才能等到相同的密文,並且這個演演算法是不可逆的,即便得到了加密以後的密文,也不可能通過解密演演算法反算出明文。這樣就可以把用戶的密碼以MD5值(或類似的其它演演算法)的方式保存起來,用戶註冊的時候,系統是把用戶輸入的密碼計算成MD5值,然後再去和系統中保存的MD5值進行比較,如果密文相同,就可以認定密碼是正確的,否則密碼錯誤。通過這樣的步驟,系統在並不知道用戶密碼明碼的情況下就可以確定用戶登錄系統的合法性。這樣不但可以避免用戶的密碼被具有系統管理員許可權的用戶知道,而且還在一定程度上增加了密碼被破解的難度。
電子簽名
MD5演演算法還可以作為一種電子簽名的方法來使用,使用MD5演演算法就可以為任何文件(不管其大小、格式、數量)產生一個獨一無二的“數字指紋”,藉助這個“數字指紋”,通過檢查文件前後MD5值是否發生了改變,就可以知道源文件是否被改動。我們在下載軟體的時候經常會發現,軟體的下載頁面上除了會提供軟體的下載地址以外,還會給出一串長長的字元串。這串字元串其實就是該軟體的MD5值,它的作用就在於下載該軟體后,對下載得到的文件用專門的軟體(如Windows MD5 check等)做一次MD5校驗,以確保我們獲得的文件與該站點提供的文件為同一文件。利用MD5演演算法來進行文件校驗的方案被大量應用到軟體下載站、論壇資料庫、系統文件安全等方面。
垃圾郵件篩選
在電子郵件使用越來越普遍的情況下,可以利用MD5演演算法在郵件接收伺服器上進行垃圾郵件的篩選,以減少此類郵件的干擾,具體思路如下:
● ● 建立一個郵件MD5值資料庫,分別儲存郵件的MD5值、允許出現的次數(假定為3)和出現次數(初值為零)。
● ● 對每一封收到的郵件,將它的正文部分進行MD5計算,得到MD5值,將這個值在資料庫中進行搜索。
● ● 如未發現相同的MD5值,說明此郵件是第一次收到,將此MD5值存入資料庫,並將出現次數置為1,轉到第五步。
● ● 如發現相同的MD5值,說明收到過同樣內容的郵件,將出現次數加1,並與允許出現次數相比較,如小於允許出現次數,就轉到第五步。否則中止接收該郵件。結束。
● ● 接收該郵件。