MD5(1)
MD5 的全稱是Message-digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc,的Ronald L. Rivest開發(fā)出來,經(jīng)MD2、MD3和MD4發(fā)展而來。它的作用是讓大容量信息在用數(shù)字簽名軟件簽署私人密鑰前被"壓縮"成一種保密的格式(就是把一個任意長度的字節(jié)串變換成一定長的大整數(shù))。不管是MD2、MD4還是MD5,它們都需要獲得一個隨機(jī)長度的信息并產(chǎn)生一個128位的信息摘要。雖然這些算法的結(jié)構(gòu)或多或少有些相似,但MD2的設(shè)計與MD4和MD5完全不同,那是因為MD2是為8位機(jī)器做過設(shè)計優(yōu)化的,而MD4和MD5卻是面向32位的電腦。這三個算法的描述和c語言源代碼在Internet RFC 1321中有詳細(xì)的描述([url]http://www.ietf.org/rfc/rfc1321.txt[/url]),這是一份最權(quán)威的文檔,由Ronald L. Rivest在1992年8月向IETF提交。
Rivest在1989年開發(fā)出MD2算法。在這個算法中,首先對信息進(jìn)行數(shù)據(jù)補(bǔ)位,使信息的字節(jié)長度是16的倍數(shù)。然后,以一個16位的檢驗和追加到信息末尾。并且根據(jù)這個新產(chǎn)生的信息計算出散列值。后來,Rogier和Chauvaud發(fā)現(xiàn)如果忽略了檢驗和將產(chǎn)生MD2沖突。MD2算法的加密后結(jié)果是唯一的--即沒有重復(fù)。
為了加強(qiáng)算法的安全性,Rivest在1990年又開發(fā)出MD4算法。MD4算法同樣需要填補(bǔ)信息以確保信息的字節(jié)長度加上448后能被512整除(信息字節(jié)長度mod 512 = 448)。然后,一個以64位二進(jìn)制表示的信息的最初長度被添加進(jìn)來。信息被處理成512位damg?rd/merkle迭代結(jié)構(gòu)的區(qū)塊,而且每個區(qū)塊要通過三個不同步驟的處理。Den boer和Bosselaers以及其他人很快的發(fā)現(xiàn)了***MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的個人電腦在幾分鐘內(nèi)找到MD4完整版本中的沖突(這個沖突實際上是一種漏洞,它將導(dǎo)致對不同的內(nèi)容進(jìn)行加密卻可能得到相同的加密后結(jié)果)。毫無疑問,MD4就此被淘汰掉了。
盡管MD4算法在安全上有個這么大的漏洞,但它對在其后才被開發(fā)出來的好幾種信息安全加密算法的出現(xiàn)卻有著不可忽視的引導(dǎo)作用。除了MD5以外,其中比較有名的還有sha-1、RIPEMD以及Haval等。
一年以后,即1991年,Rivest開發(fā)出技術(shù)上更為趨近成熟的md5算法。它在MD4的基礎(chǔ)上增加了"安全-帶子"(safety-belts)的概念。雖然MD5比MD4稍微慢一些,但卻更為安全。這個算法很明顯的由四個和MD4設(shè)計有少許不同的步驟組成。在MD5算法中,信息-摘要的大小和填充的必要條件與MD5完全相同。Den boer和Bosselaers曾發(fā)現(xiàn)MD5算法中的假沖突(pseudo-collisions),但除此之外就沒有其他被發(fā)現(xiàn)的加密后結(jié)果了。
Van oorschot和Wiener曾經(jīng)考慮過一個在散列中暴力搜尋沖突的函數(shù)(brute-force hash function),而且他們猜測一個被設(shè)計專門用來搜索MD5沖突的機(jī)器(這臺機(jī)器在1994年的制造成本大約是一百萬美元)可以平均每24天就找到一個沖突。但單從1991年到2001年這10年間,竟沒有出現(xiàn)替代MD5算法的MD6或被叫做其他什么名字的新算法這一點,我們就可以看出這個瑕疵并沒有太多的影響MD5的安全性。上面所有這些都不足以成為MD5的在實際應(yīng)用中的問題。并且,由于MD5算法的使用不需要支付任何版權(quán)費用的,所以在一般的情況下(非絕密應(yīng)用領(lǐng)域。但即便是應(yīng)用在絕密領(lǐng)域內(nèi),MD5也不失為一種非常優(yōu)秀的中間技術(shù)),MD5怎么都應(yīng)該算得上是非常安全的了。
2004年8月17日的美國加州圣巴巴拉的國際密碼學(xué)會議(Crypto’2004)上,來自中國山東大學(xué)的王小云教授做了破譯MD5、HAVAL-128、 MD4和RIPEMD算法的報告,公布了MD系列算法的破解結(jié)果。宣告了固若金湯的世界通行密碼標(biāo)準(zhǔn)MD5的堡壘轟然倒塌,引發(fā)了密碼學(xué)界的軒然大波。
?? 令世界頂尖密碼學(xué)家想象不到的是,破解MD5之后,2005年2月,王小云教授又破解了另一國際密碼SHA-1。因為SHA-1在美國等國際社會有更加廣泛的應(yīng)用,密碼被破的消息一出,在國際社會的反響可謂石破天驚。換句話說,王小云的研究成果表明了從理論上講電子簽名可以偽造,必須及時添加限制條件,或者重新選用更為安全的密碼標(biāo)準(zhǔn),以保證電子商務(wù)的安全。
MD5破解工程權(quán)威網(wǎng)站[url]http://www.md5crk.com/[/url] 是為了公開征集專門針對MD5的***而設(shè)立的,網(wǎng)站于2004年8月17日宣布:“中國研究人員發(fā)現(xiàn)了完整MD5算法的碰撞;Wang, Feng, Lai與Yu公布了MD5、MD4、HAVAL-128、RIPEMD-128幾個 Hash函數(shù)的碰撞。這是近年來密碼學(xué)領(lǐng)域最具實質(zhì)性的研究進(jìn)展。使用他們的技術(shù),在數(shù)個小時內(nèi)就可以找到MD5碰撞。……由于這個里程碑式的發(fā)現(xiàn),MD5CRK項目將在隨后48小時內(nèi)結(jié)束”。
MD5用的是哈希函數(shù),在計算機(jī)網(wǎng)絡(luò)中應(yīng)用較多的不可逆加密算法有RSA公司發(fā)明的MD5算法和由美國國家技術(shù)標(biāo)準(zhǔn)研究所建議的安全散列算法SHA.
算法的應(yīng)用 MD5的典型應(yīng)用是對一段信息(Message)產(chǎn)生信息摘要(Message-Digest),以防止被篡改。比如,在UNIX下有很多軟件在下載的時候都有一個文件名相同,文件擴(kuò)展名為.md5的文件,在這個文件中通常只有一行文本,大致結(jié)構(gòu)如:
MD5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461
這就是tanajiya.tar.gz文件的數(shù)字簽名。MD5將整個文件當(dāng)作一個大文本信息,通過其不可逆的字符串變換算法,產(chǎn)生了這個唯一的MD5信息摘要。為了讓讀者朋友對MD5的應(yīng)用有個直觀的認(rèn)識,筆者以一個比方和一個實例來簡要描述一下其工作過程:
大家都知道,地球上任何人都有自己獨一無二的指紋,這常常成為公安機(jī)關(guān)鑒別罪犯身份最值得信賴的方法;與之類似,MD5就可以為任何文件(不管其大小、格式、數(shù)量)產(chǎn)生一個同樣獨一無二的“數(shù)字指紋”,如果任何人對文件做了任何改動,其MD5值也就是對應(yīng)的“數(shù)字指紋”都會發(fā)生變化。
我們常常在某些軟件下載站點的某軟件信息中看到其MD5值,它的作用就在于我們可以在下載該軟件后,對下載回來的文件用專門的軟件(如Windows MD5 Check等)做一次MD5校驗,以確保我們獲得的文件與該站點提供的文件為同一文件。利用MD5算法來進(jìn)行文件校驗的方案被大量應(yīng)用到軟件下載站、論壇數(shù)據(jù)庫、系統(tǒng)文件安全等方面。
MD5的典型應(yīng)用是對一段Message(字節(jié)串)產(chǎn)生fingerprint(指紋),以防止被 “篡改”。舉個例子,你將一段話寫在一個叫 readme.txt文件中,并對這個readme.txt產(chǎn)生一個MD5的值并記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內(nèi)容,你對這個文件重新計算MD5時就會發(fā)現(xiàn)(兩個MD5值不相同)。如果再有一個第三方的認(rèn)證機(jī)構(gòu),用MD5還可以防止文件作者的“抵賴”,這就是所謂的數(shù)字簽名應(yīng)用。
所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統(tǒng)中的md5()函數(shù)重新設(shè)一個密碼,如admin,把生成的一串密碼覆蓋原來的就行了。
MD5 還廣泛用于操作系統(tǒng)的登陸認(rèn)證上,如Unix、各類BSD系統(tǒng)登錄密碼、數(shù)字簽名等諸多方。如在UNIX系統(tǒng)中用戶的密碼是以MD5(或其它類似的算法)經(jīng)Hash運算后存儲在文件系統(tǒng)中。當(dāng)用戶登錄的時候,系統(tǒng)把用戶輸入的密碼進(jìn)行MD5 Hash運算,然后再去和保存在文件系統(tǒng)中的MD5值進(jìn)行比較,進(jìn)而確定輸入的密碼是否正確。通過這樣的步驟,系統(tǒng)在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統(tǒng)的合法性。這可以避免用戶的密碼被具有系統(tǒng)管理員權(quán)限的用戶知道。MD5將任意長度的“字節(jié)串”映射為一個128bit的大整數(shù),并且是通過該128bit反推原始字符串是困難的,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串,從數(shù)學(xué)原理上說,是因為原始的字符串有無窮多個,這有點象不存在反函數(shù)的數(shù)學(xué)函數(shù)。所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統(tǒng)中的md5()函數(shù)重新設(shè)一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就行了。
正是因為這個原因,現(xiàn)在被***使用最多的一種破譯密碼的方法就是一種被稱為"跑字典"的方法。有兩種方法得到字典,一種是日常搜集的用做密碼的字符串表,另一種是用排列組合方法生成的,先用 MD5程序計算出這些字典項的MD5值,然后再用目標(biāo)的MD5值在這個字典中檢索。我們假設(shè)密碼的最大長度為8位字節(jié)(8 Bytes),同時密碼只能是字母和數(shù)字,共26+26+10=62個字符,排列組合出的字典的項數(shù)則是 P(62,1)+P(62,2)….+P(62,8),那也已經(jīng)是一個很天文的數(shù)字了,存儲這個字典就需要TB級的磁盤陣列,而且這種方法還有一個前提,就是能獲得目標(biāo)賬戶的密碼MD5值的情況下才可以。這種加密技術(shù)被廣泛的應(yīng)用于UNIX系統(tǒng)中,這也是為什么UNIX系統(tǒng)比一般操作系統(tǒng)更為堅固一個重要原因。
算法描述 對MD5算法簡要的敘述可以為:MD5以512位分組來處理輸入的信息,且每一分組又被劃分為16個32位子分組,經(jīng)過了一系列的處理后,算法的輸出由四個32位分組組成,將這四個32位分組級聯(lián)后將生成一個128位散列值。
在MD5算法中,首先需要對信息進(jìn)行填充,使其字節(jié)長度對512求余的結(jié)果等于448。因此,信息的字節(jié)長度(Bits Length)將被擴(kuò)展至N*512+448,即N*64+56個字節(jié)(Bytes),N為一個正整數(shù)。填充的方法如下,在信息的后面填充一個1和無數(shù)個 0,直到滿足上面的條件時才停止用0對信息的填充。然后,在在這個結(jié)果后面附加一個以64位二進(jìn)制表示的填充前信息長度。經(jīng)過這兩步的處理,現(xiàn)在的信息字節(jié)長度=N*512+448+64=(N+1)*512,即長度恰好是512的整數(shù)倍。這樣做的原因是為滿足后面處理中對信息長度的要求。
MD5中有四個32位被稱作鏈接變量(Chaining Variable)的整數(shù)參數(shù),他們分別為:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。
當(dāng)設(shè)置好這四個鏈接變量后,就開始進(jìn)入算法的四輪循環(huán)運算。循環(huán)的次數(shù)是信息中512位信息分組的數(shù)目。
將上面四個鏈接變量復(fù)制到另外四個變量中:A到a,B到b,C到c,D到d。
主循環(huán)有四輪(MD4只有三輪),每輪循環(huán)都很相似。第一輪進(jìn)行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函數(shù)運算,然后將所得結(jié)果加上第四個變量,文本的一個子分組和一個常數(shù)。再將所得結(jié)果向右環(huán)移一個不定的數(shù),并加上a、b、c或d中之一。最后用該結(jié)果取代a、b、c或d中之一。
以一下是每次操作中用到的四個非線性函數(shù)(每輪一個)。
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))
(&是與,|是或,~是非,^是異或)
這四個函數(shù)的說明:如果X、Y和Z的對應(yīng)位是獨立和均勻的,那么結(jié)果的每一位也應(yīng)是獨立和均勻的。
F是一個逐位運算的函數(shù)。即,如果X,那么Y,否則Z。函數(shù)H是逐位奇偶操作符。
假設(shè)Mj表示消息的第j個子分組(從0到15),<<
FF(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (F(b, c, d) + Mj + ti) << s
GG(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (G(b, c, d) + Mj + ti) << s
HH(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (H(b, c, d) + Mj + ti) << s
II(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (I(b, c, d) + Mj + ti) << s
這四輪(64步)是:
第一輪
FF(a, b, c, d, M0, 7, 0xd76aa478)
FF(d, a, b, c, M1, 12, 0xe8c7b756)
FF(c, d, a, b, M2, 17, 0x242070db)
FF(b, c, d, a, M3, 22, 0xc1bdceee)
FF(a, b, c, d, M4, 7, 0xf57c0faf)
FF(d, a, b, c, M5, 12, 0x4787c62a)
FF(c, d, a, b, M6, 17, 0xa8304613)
FF(b, c, d, a, M7, 22, 0xfd469501)
FF(a, b, c, d, M8, 7, 0x698098d8)
FF(d, a, b, c, M9, 12, 0x8b44f7af)
FF(c, d, a, b, M10, 17, 0xffff5bb1)
FF(b, c, d, a, M11, 22, 0x895cd7be)
FF(a, b, c, d, M12, 7, 0x6b901122)
FF(d, a, b, c, M13, 12, 0xfd987193)
FF(c, d, a, b, M14, 17, 0xa679438e)
FF(b, c, d, a, M15, 22, 0x49b40821)
第二輪
GG(a, b, c, d, M1, 5, 0xf61e2562)
GG(d, a, b, c, M6, 9, 0xc040b340)
GG(c, d, a, b, M11, 14, 0x265e5a51)
GG(b, c, d, a, M0, 20, 0xe9b6c7aa)
GG(a, b, c, d, M5, 5, 0xd62f105d)
GG(d, a, b, c, M10, 9, 0x02441453)
GG(c, d, a, b, M15, 14, 0xd8a1e681)
GG(b, c, d, a, M4, 20, 0xe7d3fbc8)
GG(a, b, c, d, M9, 5, 0x21e1cde6)
GG(d, a, b, c, M14, 9, 0xc33707d6)
GG(c, d, a, b, M3, 14, 0xf4d50d87)
GG(b, c, d, a, M8, 20, 0x455a14ed)
GG(a, b, c, d, M13, 5, 0xa9e3e905)
GG(d, a, b, c, M2, 9, 0xfcefa3f8)
GG(c, d, a, b, M7, 14, 0x676f02d9)
GG(b, c, d, a, M12, 20, 0x8d2a4c8a)
第三輪
HH(a, b, c, d, M5, 4, 0xfffa3942)
HH(d, a, b, c, M8, 11, 0x8771f681)
HH(c, d, a, b, M11, 16, 0x6d9d6122)
HH(b, c, d, a, M14, 23, 0xfde5380c)
HH(a, b, c, d, M1, 4, 0xa4beea44)
HH(d, a, b, c, M4, 11, 0x4bdecfa9)
HH(c, d, a, b, M7, 16, 0xf6bb4b60)
HH(b, c, d, a, M10, 23, 0xbebfbc70)
HH(a, b, c, d, M13, 4, 0x289b7ec6)
HH(d, a, b, c, M0, 11, 0xeaa127fa)
HH(c, d, a, b, M3, 16, 0xd4ef3085)
HH(b, c, d, a, M6, 23, 0x04881d05)
HH(a, b, c, d, M9, 4, 0xd9d4d039)
HH(d, a, b, c, M12, 11, 0xe6db99e5)
HH(c, d, a, b, M15, 16, 0x1fa27cf8)
HH(b, c, d, a, M2, 23, 0xc4ac5665)
第四輪
II(a, b, c, d, M0, 6, 0xf4292244)
II(d, a, b, c, M7, 10, 0x432aff97)
II(c, d, a, b, M14, 15, 0xab9423a7)
II(b, c, d, a, M5, 21, 0xfc93a039)
II(a, b, c, d, M12, 6, 0x655b59c3)
II(d, a, b, c, M3, 10, 0x8f0ccc92)
II(c, d, a, b, M10, 15, 0xffeff47d)
II(b, c, d, a, M1, 21, 0x85845dd1)
II(a, b, c, d, M8, 6, 0x6fa87e4f)
II(d, a, b, c, M15, 10, 0xfe2ce6e0)
II(c, d, a, b, M6, 15, 0xa3014314)
II(b, c, d, a, M13, 21, 0x4e0811a1)
II(a, b, c, d, M4, 6, 0xf7537e82)
II(d, a, b, c, M11, 10, 0xbd3af235)
II(c, d, a, b, M2, 15, 0x2ad7d2bb)
II(b, c, d, a, M9, 21, 0xeb86d391)
常數(shù)ti可以如下選擇:
在第i步中,ti是4294967296*abs(sin(i))的整數(shù)部分,i的單位是弧度。(4294967296等于2的32次方)
所有這些完成之后,將A、B、C、D分別加上a、b、c、d。然后用下一分組數(shù)據(jù)繼續(xù)運行算法,最后的輸出是A、B、C和D的級聯(lián)。
當(dāng)你按照我上面所說的方法實現(xiàn)MD5算法以后,你可以用以下幾個信息對你做出來的程序作一個簡單的測試,看看程序有沒有錯誤。
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
具體的一個MD5實現(xiàn) =============================頭文件Security.h===============================================
/*
使用方法:
char Md5Buffer[33];
CSecurity Security;
Security.MD5("a string",Md5Buffer);
執(zhí)行完成之后Md5Buffer中即存儲了由"a string"計算得到的MD5值
*/
// 下列 ifdef 塊是創(chuàng)建使從 DLL 導(dǎo)出更簡單的
// 宏的標(biāo)準(zhǔn)方法。此 DLL 中的所有文件都是用命令行上定義的 SECURITY_EXPORTS
// 符號編譯的。在使用此 DLL 的
// 任何其他項目上不應(yīng)定義此符號。這樣,源文件中包含此文件的任何其他項目都會將
// SECURITY_API 函數(shù)視為是從此 DLL 導(dǎo)入的,而此 DLL 則將用此宏定義的
// 符號視為是被導(dǎo)出的。
//在使用該類的地方包含本文件即可
#ifdef SECURITY_EXPORTS
#define SECURITY_API __declspec(dllexport)
#else
#define SECURITY_API __declspec(dllimport)
#endif
/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;
/* UINT2 defines a two byte word */
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
#define PROTO_LIST(list) list
/* MD5 context. */
typedef struct _MD5_CTX
{
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} MD5_CTX;
static unsigned char PADDING[64]= {
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
};
/* Constants for MD5Transform routine.
*/
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
/* F, G, H and I are basic MD5 functions.
*/
#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)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac);(a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define TEST_BLOCK_LEN 1000
#define TEST_BLOCK_COUNT 1000
// 此類是從 Security.dll 導(dǎo)出的
class SECURITY_API CSecurity
{
public:
CSecurity(void);
void CSecurity::MD5( const char *string ,char *lpMD5StringBuffer ) ;
private:
void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
void MD5_memcpy PROTO_LIST ((POINTER, POINTER, size_t));
void MD5_memset PROTO_LIST ((POINTER, int, size_t));
void MD5Init PROTO_LIST ((MD5_CTX *));
void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, size_t));
void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
void MDTimeTrial PROTO_LIST ((void));
void StringAddOne PROTO_LIST ((char *));
void Encode PROTO_LIST ((unsigned char *, UINT4 *, size_t));
void Decode PROTO_LIST ((UINT4 *, unsigned char *, size_t));
};
===============================Security.cpp====================================================
// Security.cpp : 定義 DLL 應(yīng)用程序的入口點。
//
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include "Security.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// 這是已導(dǎo)出類的構(gòu)造函數(shù)。
// 有關(guān)類定義的信息,請參閱 Security.h
CSecurity::CSecurity()
{
return;
}
/*
MD5 initialization. Begins an MD5 operation, writing a new context.
*/
void CSecurity::MD5Init( MD5_CTX *context )
{
context->count[0] = context->count[1] = 0;
/*
Load magic initialization constants.
*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
/*
MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void CSecurity::MD5Update(
MD5_CTX *context, /* context */
unsigned char *input, /* input block */
size_t inputLen /* length of input block */
)
{
size_t i, index, partLen;
/* Compute number of bytes mod 64 */
index = (size_t)((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((context->count[0] += ((UINT4)inputLen << 3))
< ((UINT4)inputLen << 3))
context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29);
partLen = 64 - index;
/* Transform as many times as possible. */
if (inputLen >= partLen) {
MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD5Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD5Transform (context->state, &input);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)&input,
inputLen-i);
}
/*
MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void CSecurity::MD5Final(
unsigned char digest[16], /* message digest */
MD5_CTX *context /* context */
)
{
unsigned char bits[8];
size_t index, padLen;
/* Save number of bits */
Encode (bits, context->count, 8);
/* Pad out to 56 mod 64. */
index = (size_t)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen);
/* Append length (before padding) */
MD5Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
/* Zeroize sensitive information. */
MD5_memset ((POINTER)context, 0, sizeof (*context));
}
/*
MD5 basic transformation. Transforms state based on block.
*/
void CSecurity::MD5Transform(
UINT4 state[4],
unsigned char block[64]
)
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
Rivest在1989年開發(fā)出MD2算法。在這個算法中,首先對信息進(jìn)行數(shù)據(jù)補(bǔ)位,使信息的字節(jié)長度是16的倍數(shù)。然后,以一個16位的檢驗和追加到信息末尾。并且根據(jù)這個新產(chǎn)生的信息計算出散列值。后來,Rogier和Chauvaud發(fā)現(xiàn)如果忽略了檢驗和將產(chǎn)生MD2沖突。MD2算法的加密后結(jié)果是唯一的--即沒有重復(fù)。
為了加強(qiáng)算法的安全性,Rivest在1990年又開發(fā)出MD4算法。MD4算法同樣需要填補(bǔ)信息以確保信息的字節(jié)長度加上448后能被512整除(信息字節(jié)長度mod 512 = 448)。然后,一個以64位二進(jìn)制表示的信息的最初長度被添加進(jìn)來。信息被處理成512位damg?rd/merkle迭代結(jié)構(gòu)的區(qū)塊,而且每個區(qū)塊要通過三個不同步驟的處理。Den boer和Bosselaers以及其他人很快的發(fā)現(xiàn)了***MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的個人電腦在幾分鐘內(nèi)找到MD4完整版本中的沖突(這個沖突實際上是一種漏洞,它將導(dǎo)致對不同的內(nèi)容進(jìn)行加密卻可能得到相同的加密后結(jié)果)。毫無疑問,MD4就此被淘汰掉了。
盡管MD4算法在安全上有個這么大的漏洞,但它對在其后才被開發(fā)出來的好幾種信息安全加密算法的出現(xiàn)卻有著不可忽視的引導(dǎo)作用。除了MD5以外,其中比較有名的還有sha-1、RIPEMD以及Haval等。
一年以后,即1991年,Rivest開發(fā)出技術(shù)上更為趨近成熟的md5算法。它在MD4的基礎(chǔ)上增加了"安全-帶子"(safety-belts)的概念。雖然MD5比MD4稍微慢一些,但卻更為安全。這個算法很明顯的由四個和MD4設(shè)計有少許不同的步驟組成。在MD5算法中,信息-摘要的大小和填充的必要條件與MD5完全相同。Den boer和Bosselaers曾發(fā)現(xiàn)MD5算法中的假沖突(pseudo-collisions),但除此之外就沒有其他被發(fā)現(xiàn)的加密后結(jié)果了。
Van oorschot和Wiener曾經(jīng)考慮過一個在散列中暴力搜尋沖突的函數(shù)(brute-force hash function),而且他們猜測一個被設(shè)計專門用來搜索MD5沖突的機(jī)器(這臺機(jī)器在1994年的制造成本大約是一百萬美元)可以平均每24天就找到一個沖突。但單從1991年到2001年這10年間,竟沒有出現(xiàn)替代MD5算法的MD6或被叫做其他什么名字的新算法這一點,我們就可以看出這個瑕疵并沒有太多的影響MD5的安全性。上面所有這些都不足以成為MD5的在實際應(yīng)用中的問題。并且,由于MD5算法的使用不需要支付任何版權(quán)費用的,所以在一般的情況下(非絕密應(yīng)用領(lǐng)域。但即便是應(yīng)用在絕密領(lǐng)域內(nèi),MD5也不失為一種非常優(yōu)秀的中間技術(shù)),MD5怎么都應(yīng)該算得上是非常安全的了。
2004年8月17日的美國加州圣巴巴拉的國際密碼學(xué)會議(Crypto’2004)上,來自中國山東大學(xué)的王小云教授做了破譯MD5、HAVAL-128、 MD4和RIPEMD算法的報告,公布了MD系列算法的破解結(jié)果。宣告了固若金湯的世界通行密碼標(biāo)準(zhǔn)MD5的堡壘轟然倒塌,引發(fā)了密碼學(xué)界的軒然大波。
?? 令世界頂尖密碼學(xué)家想象不到的是,破解MD5之后,2005年2月,王小云教授又破解了另一國際密碼SHA-1。因為SHA-1在美國等國際社會有更加廣泛的應(yīng)用,密碼被破的消息一出,在國際社會的反響可謂石破天驚。換句話說,王小云的研究成果表明了從理論上講電子簽名可以偽造,必須及時添加限制條件,或者重新選用更為安全的密碼標(biāo)準(zhǔn),以保證電子商務(wù)的安全。
MD5破解工程權(quán)威網(wǎng)站[url]http://www.md5crk.com/[/url] 是為了公開征集專門針對MD5的***而設(shè)立的,網(wǎng)站于2004年8月17日宣布:“中國研究人員發(fā)現(xiàn)了完整MD5算法的碰撞;Wang, Feng, Lai與Yu公布了MD5、MD4、HAVAL-128、RIPEMD-128幾個 Hash函數(shù)的碰撞。這是近年來密碼學(xué)領(lǐng)域最具實質(zhì)性的研究進(jìn)展。使用他們的技術(shù),在數(shù)個小時內(nèi)就可以找到MD5碰撞。……由于這個里程碑式的發(fā)現(xiàn),MD5CRK項目將在隨后48小時內(nèi)結(jié)束”。
MD5用的是哈希函數(shù),在計算機(jī)網(wǎng)絡(luò)中應(yīng)用較多的不可逆加密算法有RSA公司發(fā)明的MD5算法和由美國國家技術(shù)標(biāo)準(zhǔn)研究所建議的安全散列算法SHA.
算法的應(yīng)用 MD5的典型應(yīng)用是對一段信息(Message)產(chǎn)生信息摘要(Message-Digest),以防止被篡改。比如,在UNIX下有很多軟件在下載的時候都有一個文件名相同,文件擴(kuò)展名為.md5的文件,在這個文件中通常只有一行文本,大致結(jié)構(gòu)如:
MD5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461
這就是tanajiya.tar.gz文件的數(shù)字簽名。MD5將整個文件當(dāng)作一個大文本信息,通過其不可逆的字符串變換算法,產(chǎn)生了這個唯一的MD5信息摘要。為了讓讀者朋友對MD5的應(yīng)用有個直觀的認(rèn)識,筆者以一個比方和一個實例來簡要描述一下其工作過程:
大家都知道,地球上任何人都有自己獨一無二的指紋,這常常成為公安機(jī)關(guān)鑒別罪犯身份最值得信賴的方法;與之類似,MD5就可以為任何文件(不管其大小、格式、數(shù)量)產(chǎn)生一個同樣獨一無二的“數(shù)字指紋”,如果任何人對文件做了任何改動,其MD5值也就是對應(yīng)的“數(shù)字指紋”都會發(fā)生變化。
我們常常在某些軟件下載站點的某軟件信息中看到其MD5值,它的作用就在于我們可以在下載該軟件后,對下載回來的文件用專門的軟件(如Windows MD5 Check等)做一次MD5校驗,以確保我們獲得的文件與該站點提供的文件為同一文件。利用MD5算法來進(jìn)行文件校驗的方案被大量應(yīng)用到軟件下載站、論壇數(shù)據(jù)庫、系統(tǒng)文件安全等方面。
MD5的典型應(yīng)用是對一段Message(字節(jié)串)產(chǎn)生fingerprint(指紋),以防止被 “篡改”。舉個例子,你將一段話寫在一個叫 readme.txt文件中,并對這個readme.txt產(chǎn)生一個MD5的值并記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內(nèi)容,你對這個文件重新計算MD5時就會發(fā)現(xiàn)(兩個MD5值不相同)。如果再有一個第三方的認(rèn)證機(jī)構(gòu),用MD5還可以防止文件作者的“抵賴”,這就是所謂的數(shù)字簽名應(yīng)用。
所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統(tǒng)中的md5()函數(shù)重新設(shè)一個密碼,如admin,把生成的一串密碼覆蓋原來的就行了。
MD5 還廣泛用于操作系統(tǒng)的登陸認(rèn)證上,如Unix、各類BSD系統(tǒng)登錄密碼、數(shù)字簽名等諸多方。如在UNIX系統(tǒng)中用戶的密碼是以MD5(或其它類似的算法)經(jīng)Hash運算后存儲在文件系統(tǒng)中。當(dāng)用戶登錄的時候,系統(tǒng)把用戶輸入的密碼進(jìn)行MD5 Hash運算,然后再去和保存在文件系統(tǒng)中的MD5值進(jìn)行比較,進(jìn)而確定輸入的密碼是否正確。通過這樣的步驟,系統(tǒng)在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統(tǒng)的合法性。這可以避免用戶的密碼被具有系統(tǒng)管理員權(quán)限的用戶知道。MD5將任意長度的“字節(jié)串”映射為一個128bit的大整數(shù),并且是通過該128bit反推原始字符串是困難的,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串,從數(shù)學(xué)原理上說,是因為原始的字符串有無窮多個,這有點象不存在反函數(shù)的數(shù)學(xué)函數(shù)。所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統(tǒng)中的md5()函數(shù)重新設(shè)一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就行了。
正是因為這個原因,現(xiàn)在被***使用最多的一種破譯密碼的方法就是一種被稱為"跑字典"的方法。有兩種方法得到字典,一種是日常搜集的用做密碼的字符串表,另一種是用排列組合方法生成的,先用 MD5程序計算出這些字典項的MD5值,然后再用目標(biāo)的MD5值在這個字典中檢索。我們假設(shè)密碼的最大長度為8位字節(jié)(8 Bytes),同時密碼只能是字母和數(shù)字,共26+26+10=62個字符,排列組合出的字典的項數(shù)則是 P(62,1)+P(62,2)….+P(62,8),那也已經(jīng)是一個很天文的數(shù)字了,存儲這個字典就需要TB級的磁盤陣列,而且這種方法還有一個前提,就是能獲得目標(biāo)賬戶的密碼MD5值的情況下才可以。這種加密技術(shù)被廣泛的應(yīng)用于UNIX系統(tǒng)中,這也是為什么UNIX系統(tǒng)比一般操作系統(tǒng)更為堅固一個重要原因。
算法描述 對MD5算法簡要的敘述可以為:MD5以512位分組來處理輸入的信息,且每一分組又被劃分為16個32位子分組,經(jīng)過了一系列的處理后,算法的輸出由四個32位分組組成,將這四個32位分組級聯(lián)后將生成一個128位散列值。
在MD5算法中,首先需要對信息進(jìn)行填充,使其字節(jié)長度對512求余的結(jié)果等于448。因此,信息的字節(jié)長度(Bits Length)將被擴(kuò)展至N*512+448,即N*64+56個字節(jié)(Bytes),N為一個正整數(shù)。填充的方法如下,在信息的后面填充一個1和無數(shù)個 0,直到滿足上面的條件時才停止用0對信息的填充。然后,在在這個結(jié)果后面附加一個以64位二進(jìn)制表示的填充前信息長度。經(jīng)過這兩步的處理,現(xiàn)在的信息字節(jié)長度=N*512+448+64=(N+1)*512,即長度恰好是512的整數(shù)倍。這樣做的原因是為滿足后面處理中對信息長度的要求。
MD5中有四個32位被稱作鏈接變量(Chaining Variable)的整數(shù)參數(shù),他們分別為:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。
當(dāng)設(shè)置好這四個鏈接變量后,就開始進(jìn)入算法的四輪循環(huán)運算。循環(huán)的次數(shù)是信息中512位信息分組的數(shù)目。
將上面四個鏈接變量復(fù)制到另外四個變量中:A到a,B到b,C到c,D到d。
主循環(huán)有四輪(MD4只有三輪),每輪循環(huán)都很相似。第一輪進(jìn)行16次操作。每次操作對a、b、c和d中的其中三個作一次非線性函數(shù)運算,然后將所得結(jié)果加上第四個變量,文本的一個子分組和一個常數(shù)。再將所得結(jié)果向右環(huán)移一個不定的數(shù),并加上a、b、c或d中之一。最后用該結(jié)果取代a、b、c或d中之一。
以一下是每次操作中用到的四個非線性函數(shù)(每輪一個)。
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))
(&是與,|是或,~是非,^是異或)
這四個函數(shù)的說明:如果X、Y和Z的對應(yīng)位是獨立和均勻的,那么結(jié)果的每一位也應(yīng)是獨立和均勻的。
F是一個逐位運算的函數(shù)。即,如果X,那么Y,否則Z。函數(shù)H是逐位奇偶操作符。
假設(shè)Mj表示消息的第j個子分組(從0到15),<<
FF(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (F(b, c, d) + Mj + ti) << s
GG(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (G(b, c, d) + Mj + ti) << s
HH(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (H(b, c, d) + Mj + ti) << s
II(a, b, c, d, Mj, s, ti)表示 a = b + ((a + (I(b, c, d) + Mj + ti) << s
這四輪(64步)是:
第一輪
FF(a, b, c, d, M0, 7, 0xd76aa478)
FF(d, a, b, c, M1, 12, 0xe8c7b756)
FF(c, d, a, b, M2, 17, 0x242070db)
FF(b, c, d, a, M3, 22, 0xc1bdceee)
FF(a, b, c, d, M4, 7, 0xf57c0faf)
FF(d, a, b, c, M5, 12, 0x4787c62a)
FF(c, d, a, b, M6, 17, 0xa8304613)
FF(b, c, d, a, M7, 22, 0xfd469501)
FF(a, b, c, d, M8, 7, 0x698098d8)
FF(d, a, b, c, M9, 12, 0x8b44f7af)
FF(c, d, a, b, M10, 17, 0xffff5bb1)
FF(b, c, d, a, M11, 22, 0x895cd7be)
FF(a, b, c, d, M12, 7, 0x6b901122)
FF(d, a, b, c, M13, 12, 0xfd987193)
FF(c, d, a, b, M14, 17, 0xa679438e)
FF(b, c, d, a, M15, 22, 0x49b40821)
第二輪
GG(a, b, c, d, M1, 5, 0xf61e2562)
GG(d, a, b, c, M6, 9, 0xc040b340)
GG(c, d, a, b, M11, 14, 0x265e5a51)
GG(b, c, d, a, M0, 20, 0xe9b6c7aa)
GG(a, b, c, d, M5, 5, 0xd62f105d)
GG(d, a, b, c, M10, 9, 0x02441453)
GG(c, d, a, b, M15, 14, 0xd8a1e681)
GG(b, c, d, a, M4, 20, 0xe7d3fbc8)
GG(a, b, c, d, M9, 5, 0x21e1cde6)
GG(d, a, b, c, M14, 9, 0xc33707d6)
GG(c, d, a, b, M3, 14, 0xf4d50d87)
GG(b, c, d, a, M8, 20, 0x455a14ed)
GG(a, b, c, d, M13, 5, 0xa9e3e905)
GG(d, a, b, c, M2, 9, 0xfcefa3f8)
GG(c, d, a, b, M7, 14, 0x676f02d9)
GG(b, c, d, a, M12, 20, 0x8d2a4c8a)
第三輪
HH(a, b, c, d, M5, 4, 0xfffa3942)
HH(d, a, b, c, M8, 11, 0x8771f681)
HH(c, d, a, b, M11, 16, 0x6d9d6122)
HH(b, c, d, a, M14, 23, 0xfde5380c)
HH(a, b, c, d, M1, 4, 0xa4beea44)
HH(d, a, b, c, M4, 11, 0x4bdecfa9)
HH(c, d, a, b, M7, 16, 0xf6bb4b60)
HH(b, c, d, a, M10, 23, 0xbebfbc70)
HH(a, b, c, d, M13, 4, 0x289b7ec6)
HH(d, a, b, c, M0, 11, 0xeaa127fa)
HH(c, d, a, b, M3, 16, 0xd4ef3085)
HH(b, c, d, a, M6, 23, 0x04881d05)
HH(a, b, c, d, M9, 4, 0xd9d4d039)
HH(d, a, b, c, M12, 11, 0xe6db99e5)
HH(c, d, a, b, M15, 16, 0x1fa27cf8)
HH(b, c, d, a, M2, 23, 0xc4ac5665)
第四輪
II(a, b, c, d, M0, 6, 0xf4292244)
II(d, a, b, c, M7, 10, 0x432aff97)
II(c, d, a, b, M14, 15, 0xab9423a7)
II(b, c, d, a, M5, 21, 0xfc93a039)
II(a, b, c, d, M12, 6, 0x655b59c3)
II(d, a, b, c, M3, 10, 0x8f0ccc92)
II(c, d, a, b, M10, 15, 0xffeff47d)
II(b, c, d, a, M1, 21, 0x85845dd1)
II(a, b, c, d, M8, 6, 0x6fa87e4f)
II(d, a, b, c, M15, 10, 0xfe2ce6e0)
II(c, d, a, b, M6, 15, 0xa3014314)
II(b, c, d, a, M13, 21, 0x4e0811a1)
II(a, b, c, d, M4, 6, 0xf7537e82)
II(d, a, b, c, M11, 10, 0xbd3af235)
II(c, d, a, b, M2, 15, 0x2ad7d2bb)
II(b, c, d, a, M9, 21, 0xeb86d391)
常數(shù)ti可以如下選擇:
在第i步中,ti是4294967296*abs(sin(i))的整數(shù)部分,i的單位是弧度。(4294967296等于2的32次方)
所有這些完成之后,將A、B、C、D分別加上a、b、c、d。然后用下一分組數(shù)據(jù)繼續(xù)運行算法,最后的輸出是A、B、C和D的級聯(lián)。
當(dāng)你按照我上面所說的方法實現(xiàn)MD5算法以后,你可以用以下幾個信息對你做出來的程序作一個簡單的測試,看看程序有沒有錯誤。
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
具體的一個MD5實現(xiàn) =============================頭文件Security.h===============================================
/*
使用方法:
char Md5Buffer[33];
CSecurity Security;
Security.MD5("a string",Md5Buffer);
執(zhí)行完成之后Md5Buffer中即存儲了由"a string"計算得到的MD5值
*/
// 下列 ifdef 塊是創(chuàng)建使從 DLL 導(dǎo)出更簡單的
// 宏的標(biāo)準(zhǔn)方法。此 DLL 中的所有文件都是用命令行上定義的 SECURITY_EXPORTS
// 符號編譯的。在使用此 DLL 的
// 任何其他項目上不應(yīng)定義此符號。這樣,源文件中包含此文件的任何其他項目都會將
// SECURITY_API 函數(shù)視為是從此 DLL 導(dǎo)入的,而此 DLL 則將用此宏定義的
// 符號視為是被導(dǎo)出的。
//在使用該類的地方包含本文件即可
#ifdef SECURITY_EXPORTS
#define SECURITY_API __declspec(dllexport)
#else
#define SECURITY_API __declspec(dllimport)
#endif
/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;
/* UINT2 defines a two byte word */
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
#define PROTO_LIST(list) list
/* MD5 context. */
typedef struct _MD5_CTX
{
UINT4 state[4]; /* state (ABCD) */
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} MD5_CTX;
static unsigned char PADDING[64]= {
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
};
/* Constants for MD5Transform routine.
*/
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
/* F, G, H and I are basic MD5 functions.
*/
#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)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac);(a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); }
#define TEST_BLOCK_LEN 1000
#define TEST_BLOCK_COUNT 1000
// 此類是從 Security.dll 導(dǎo)出的
class SECURITY_API CSecurity
{
public:
CSecurity(void);
void CSecurity::MD5( const char *string ,char *lpMD5StringBuffer ) ;
private:
void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
void MD5_memcpy PROTO_LIST ((POINTER, POINTER, size_t));
void MD5_memset PROTO_LIST ((POINTER, int, size_t));
void MD5Init PROTO_LIST ((MD5_CTX *));
void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, size_t));
void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
void MDTimeTrial PROTO_LIST ((void));
void StringAddOne PROTO_LIST ((char *));
void Encode PROTO_LIST ((unsigned char *, UINT4 *, size_t));
void Decode PROTO_LIST ((UINT4 *, unsigned char *, size_t));
};
===============================Security.cpp====================================================
// Security.cpp : 定義 DLL 應(yīng)用程序的入口點。
//
#include "stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include "Security.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// 這是已導(dǎo)出類的構(gòu)造函數(shù)。
// 有關(guān)類定義的信息,請參閱 Security.h
CSecurity::CSecurity()
{
return;
}
/*
MD5 initialization. Begins an MD5 operation, writing a new context.
*/
void CSecurity::MD5Init( MD5_CTX *context )
{
context->count[0] = context->count[1] = 0;
/*
Load magic initialization constants.
*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
/*
MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void CSecurity::MD5Update(
MD5_CTX *context, /* context */
unsigned char *input, /* input block */
size_t inputLen /* length of input block */
)
{
size_t i, index, partLen;
/* Compute number of bytes mod 64 */
index = (size_t)((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((context->count[0] += ((UINT4)inputLen << 3))
< ((UINT4)inputLen << 3))
context->count[1]++;
context->count[1] += ((UINT4)inputLen >> 29);
partLen = 64 - index;
/* Transform as many times as possible. */
if (inputLen >= partLen) {
MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)input, partLen);
MD5Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD5Transform (context->state, &input);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
MD5_memcpy
((POINTER)&context->buffer[index], (POINTER)&input,
inputLen-i);
}
/*
MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void CSecurity::MD5Final(
unsigned char digest[16], /* message digest */
MD5_CTX *context /* context */
)
{
unsigned char bits[8];
size_t index, padLen;
/* Save number of bits */
Encode (bits, context->count, 8);
/* Pad out to 56 mod 64. */
index = (size_t)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen);
/* Append length (before padding) */
MD5Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
/* Zeroize sensitive information. */
MD5_memset ((POINTER)context, 0, sizeof (*context));
}
/*
MD5 basic transformation. Transforms state based on block.
*/
void CSecurity::MD5Transform(
UINT4 state[4],
unsigned char block[64]
)
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
轉(zhuǎn)載于:https://blog.51cto.com/movie0312/115957
總結(jié)
- 上一篇: SQL 2005完全卸载,重新安装
- 下一篇: Cisco对中国CCIE认证下重手了