MD5加密

Message Digest Algorithm MD5(中文名爲消息摘要算法第五版)爲計算機安全領域普遍使用的一種散列函數,用以提供消息的完整性保護。該算法的文件號爲RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)。
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用於確保信息傳輸完整一致。是計算機普遍使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言廣泛已有MD5實現。將數據(如漢字)運算爲另外一固定長度值,是雜湊算法的基礎原理,MD5的前身有MD二、MD3和MD4。
MD5算法具備如下特色:
一、壓縮性:任意長度的數據,算出的MD5值長度都是固定的。
二、容易計算:從原數據計算出MD5值很容易。
三、抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所獲得的MD5值都有很大區別。
四、強抗碰撞:已知原數據和其MD5值,想找到一個具備相同MD5值的數據(即僞造數據)是很是困難的。
MD5的做用是讓大容量信息在用數字簽名軟件簽署私人密鑰前被"壓縮"成一種保密的格式(就是把一個任意長度的字節串變換成必定長的十六進制數字串)。除了MD5之外,其中比較有名的還有sha-一、RIPEMD以及Haval等。
中文名
消息摘要算法
外文名
Message Digest Algorithm MD5
別    稱
摘要算法
提出時間
1991年
應用學科
信息技術,計算機科學
適用領域範圍
軟件下載站、論壇數據庫、系統文件安全
目錄
1 發展歷史
▪ MD2
▪ MD4
▪ MD5
2 MD5應用
▪ 一致性驗證
▪ 數字簽名
▪ 安全訪問認證
3 算法原理
▪ 原理
▪ MD5加密字符串實例
▪ C++實現
▪ JAVA實現
▪ VB2010實現
▪ JavaScript實現
▪ 僞代碼實現
4 弱點
發展歷史
編輯
MD2
Rivest在1989年開發出MD2算法。在這個算法中,首先對信息進行數據補位,使信息的字節長度是16的倍數。而後,以一個16位的檢驗和追加到信息末尾,而且根據這個新產生的信息計算出散列值。後來,Rogier和Chauvaud發現若是忽略了檢驗將和MD2產生衝突。MD2算法加密後結果是惟一的(即不一樣信息加密後的結果不一樣)。
MD4
爲了加
MD5
強算法的安全性,Rivest在1990年又開發出MD4算法。MD4算法一樣須要填補信息以確保信息的比特位長度減去448後能被512整除(信息比特位長度mod 512 = 448)。而後,一個以64位二進制表示的信息的最初長度被添加進來。信息被處理成512位damg?rd/merkle迭代結構的區塊,並且每一個區塊要經過三個不一樣步驟的處理。Den boer和Bosselaers以及其餘人很快的發現了攻擊MD4版本中第一步和第三步的漏洞。Dobbertin向你們演示瞭如何利用一部普通的我的電腦在幾分鐘內找到MD4完整版本中的衝突(這個衝突其實是一種漏洞,它將致使對不一樣的內容進行加密卻可能獲得相同的加密後結果)。毫無疑問,MD4就此被淘汰掉了。
儘管MD4算法在安全上有個這麼大的漏洞,但它對在其後才被開發出來的好幾種信息安全加密算法的出現卻有着不可忽視的引導做用。
MD5
1991年,Rivest開發出技術上更爲趨近成熟的md5算法。它在MD4的基礎上增長了"安全-帶子"(safety-belts)的概念。雖然MD5比MD4複雜度大一些,但卻更爲安全。這個算法很明顯的由四個和MD4設計有少量不一樣的步驟組成。在MD5算法中,信息-摘要的大小和填充的必要條件與MD4徹底相同。Den boer和Bosselaers曾發現MD5算法中的假衝突(pseudo-collisions),但除此以外就沒有其餘被發現的加密後結果了。
MD5應用
編輯
一致性驗證
MD5的
md5
典型應用是對一段信息(Message)產生信息摘要(Message-Digest),以防止被篡改。好比,在Unix下有不少軟件在下載的時候都有一個文件名相同,文件擴展名爲.md5的文件,在這個文件中一般只有一行文本,大體結構如:
MD5 (tanajiya.tar.gz) = 38b8c2c1093dd0fec383a9d9ac940515
這就是tanajiya.tar.gz文件的數字簽名。MD5將整個文件看成一個大文本信息,經過其不可逆的字符串變換算法,產生了這個惟一的MD5信息摘要。爲了讓讀者朋友對MD5的應用有個直觀的認識,筆者以一個比方和一個實例來簡要描述一下其工做過程:
你們都知道,地球上任何人都有本身獨一無二的指紋,這經常成爲司法機關鑑別罪犯身份最值得信賴的方法;與之相似,MD5就能夠爲任何文件(無論其大小、格式、數量)產生一個一樣獨一無二的
md5
「數字指紋」,若是任何人對文件作了任何改動,其MD5值也就是對應的「數字指紋」都會發生變化。
咱們經常在某些軟件下載站點的某軟件信息中看到其MD5值,它的做用就在於咱們能夠在下載該軟件後,對下載回來的文件用專門的軟件(如Windows MD5 Check等)作一次MD5校驗,以確保咱們得到的文件與該站點提供的文件爲同一文件。
具體來講文件的MD5值就像是這個文件的「數字指紋」。每一個文件的MD5值是不一樣的,若是任何人對文件作了任何改動,其MD5值也就是對應的「數字指紋」就會發生變化。好比下載服務器針對一個文件預先提供一個MD5值,用戶下載完該文件後,用我這個算法從新計算下載文件的MD5值,經過比較這兩個值是否相同,就能判斷下載的文件是否出錯,或者說下載的文件是否被篡改了。
利用MD5算法來進行文件校驗的方案被大量應用到軟件下載站、論壇數據庫、系統文件安全等方面。
數字簽名
MD5的典型應用是對一段Message(字節串)產生fingerprint(指紋),以防止被「篡改」。舉個例子,你將一段話寫在一個叫 readme.txt文件中,並對這個readme.txt產生一個MD5的值並記錄在案,而後你能夠傳播這個文件給別人,別人若是修改了文件中的任何內容,你對這個文件從新計算MD5時就會發現(兩個MD5值不相同)。若是再有一個第三方的認證機構,用MD5還能夠防止文件做者的「抵賴」,這就是所謂的數字簽名應用。
安全訪問認證
MD5還普遍用於操做系統的登錄認證上,如Unix、各種BSD系統登陸密碼、數字簽名等諸多方面。如在Unix系統中用戶的密碼是以MD5(或其它相似的算法)經Hash運算後存儲在文件系統中。當用戶登陸的時候,系統把用戶輸入的密碼進行MD5 Hash運算,而後再去和保存在文件系統中的MD5值進行比較,進而肯定輸入的密碼是否正確。經過這樣的步驟,系統在並不知道用戶密碼的明碼的狀況下就能夠肯定用戶登陸系統的合法性。這能夠避免用戶的密碼被具備系統管理員權限的用戶知道。MD5將任意長度的「字節串」映射爲一個128bit的大整數,而且是經過該128bit反推原始字符串是困難的,換句話說就是,即便你看到源程序和算法描述,也沒法將一個MD5的值變換回原始的字符串,從數學原理上說,是由於原始的字符串有無窮多個,這有點象不存在反函數的數學函數。因此,要遇到了md5密碼的問題,比較好的辦法是:你能夠用這個系統中的md5()函數從新設一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就好了。
正是由於這個緣由,如今被黑客使用最多的一種破譯密碼的方法就是一種被稱爲"跑字典"的方法。有兩種方法獲得字典,一種是平常蒐集的用作密碼的字符串表,另外一種是用排列組合方法生成的,先用MD5程序計算出這些字典項的MD5值,而後再用目標的MD5值在這個字典中檢索。咱們假設密碼的最大長度爲8位字節(8 Bytes),同時密碼只能是字母和數字,共26+26+10=62個字節,排列組合出的字典的項數則是P(62,1)+P(62,2)….+P(62,8),那也已是一個很天文的數字了,存儲這個字典就須要TB級的磁盤陣列,並且這種方法還有一個前提,就是能得到目標帳戶的密碼MD5值的狀況下才能夠。這種加密技術被普遍的應用於Unix系統中,這也是爲何Unix系統比通常操做系統更爲堅固一個重要緣由。
算法原理
編輯
原理
對MD5算法簡要的敘述能夠爲:MD5以512位分組來處理輸入的信息,且每一分組又被劃分爲16個32位子分組,通過了一系列的處理後,算法的輸出由四個32位分組組成,將這四個32位分組級聯後將生成一個128位散列值。
整體流程以下圖所示,

 表示第i個分組,每次的運算都由前一輪的128位結果值和第i塊512bit值進行運算。
MD5算法的總體流程圖
1.填充
在MD5算法中,首先須要對信息進行填充,使其位長對512求餘的結果等於448,而且填充必須進行,即便其位長對512求餘的結果等於448。所以,信息的位長(Bits Length)將被擴展至N*512+448,N爲一個非負整數,N能夠是零。
填充的方法以下:
1) 在信息的後面填充一個1和無數個0,直到知足上面的條件時才中止用0對信息的填充。
2) 在這個結果後面附加一個以64位二進制表示的填充前信息長度(單位爲Bit),若是二
進製表示的填充前信息長度超過64位,則取低64位。
通過這兩步的處理,信息的位長=N*512+448+64=(N+1)*512,即長度剛好是512的整數倍。這樣作的緣由是爲知足後面處理中對信息長度的要求。
2. 初始化變量
初始的128位值爲初試連接變量,這些參數用於第一輪的運算,以大端字節序來表示,他們分別爲: A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。
(每個變量給出的數值是高字節存於內存低地址,低字節存於內存高地址,即大端字節序。在程序中變量A、B、C、D的值分別爲0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476)
3. 處理分組數據
每一分組的算法流程以下:
第一分組須要將上面四個連接變量複製到另外四個變量中:A到a,B到b,C到c,D到d。從第二分組開始的變量爲上一分組的運算結果,即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) )
(&是與(And),|是或(Or),~是非(Not),^是異或(Xor))
這四個函數的說明:若是X、Y和Z的對應位是獨立和均勻的,那麼結果的每一位也應是獨立和均勻的。
F是一個逐位運算的函數。即,若是X,那麼Y,不然Z。函數H是逐位奇偶操做符。
假設Mj表示消息的第j個子分組(從0到15),常數ti是4294967296*abs( sin(i) )的整數部分,i 取值從1到64,單位是弧度。(4294967296=232)
現定義:
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 )
全部這些完成以後,將a、b、c、d分別在原來基礎上再加上A、B、C、D。
即a = a + A,b = b + B,c = c + C,d = d + D
而後用下一分組數據繼續運行以上算法。
4. 輸出
最後的輸出是a、b、c和d的級聯。
當你按照我上面所說的方法實現MD5算法之後,你能夠用如下幾個信息對你作出來的程序做一個簡單的測試,看看程序有沒有錯誤。
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") =
f29939a25efabaef3b87e2cbfe641315
MD5 ("8a683566bcc7801226b3d8b0cf35fd97") =cf2cb5c89c5e5eeebef4a76becddfcfd
MD5加密字符串實例
現以字符串「jklmn」爲例。
該字符串在內存中表示爲:6A 6B 6C 6D 6E(從左到右爲低地址到高地址,後同),信息長度爲40 bits, 即0x28。
對其填充,填充至448位,即56字節。結果爲:
6A 6B 6C 6D 6E 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
剩下64位,即8字節填充填充前信息位長,按小端字節序填充剩下的8字節,結果爲。
6A 6B 6C 6D 6E 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00
(64字節,512 bits)
初始化A、B、C、D四個變量。
將這64字節填充後數據分紅16個小組(程序中對應爲16個數組),即:
M0:6A 6B 6C 6D (這是內存中的順序,按照小端字節序原則,對應數組M(0)的值爲0x6D6C6B6A,下同)
M1:6E 80 00 00
M2:00 00 00 00
.....
M14:28 00 00 00
M15:00 00 00 00
通過「3. 分組數據處理」後,a、b、c、d值分別爲0xD8523F60、0x837E014四、0x517726CA、0x1BB6E5FE
在內存中爲a:60 3F 52 D8
b:44 01 7E 83
c:CA 26 77 51
d:FE E5 B6 1B
a、b、c、d按內存順序輸出即爲最終結果:603F52D844017E83CA267751FEE5B61B。這就是字符串「jklmn」的MD5值。
C++實現php


#include<iostream>
#include<string>
using namespace std;
#define shift(x, n) (((x) << (n)) | ((x) >> (32-(n))))//右移的時候,高位必定要補零,而不是補充符號位
#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)))
#define A 0x67452301
#define B 0xefcdab89
#define C 0x98badcfe
#define D 0x10325476
//strBaye的長度
unsigned int strlength;
//A,B,C,D的臨時變量
unsigned int atemp;
unsigned int btemp;
unsigned int ctemp;
unsigned int dtemp;
//常量ti unsigned int(abs(sin(i+1))*(2pow32))
const unsigned int k[]={
        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};
//向左位移數
const unsigned int s[]={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};
const char str16[]="0123456789abcdef";
void mainLoop(unsigned int M[])
{
    unsigned int f,g;
    unsigned int a=atemp;
    unsigned int b=btemp;
    unsigned int c=ctemp;
    unsigned int d=dtemp;
    for (unsigned int i = 0; i < 64; i++)
    {
        if(i<16){
            f=F(b,c,d);
            g=i;
        }else if (i<32)
        {
            f=G(b,c,d);
            g=(5*i+1)%16;
        }else if(i<48){
            f=H(b,c,d);
            g=(3*i+5)%16;
        }else{
            f=I(b,c,d);
            g=(7*i)%16;
        }
        unsigned int tmp=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;
}
/*
*填充函數
*處理後應知足bits≡448(mod512),字節就是bytes≡56(mode64)
*填充方式爲先加一個1,其它位補零
*最後加上64位的原來長度
*/
unsigned int* add(string str)
{
    unsigned int num=((str.length()+8)/64)+1;//以512位,64個字節爲一組
    unsigned int *strByte=new unsigned int[num*16];    //64/4=16,因此有16個整數
    strlength=num*16;
    for (unsigned int i = 0; i < num*16; i++)
        strByte[i]=0;
    for (unsigned int i=0; i <str.length(); i++)
    {
        strByte[i>>2]|=(str[i])<<((i%4)*8);//一個整數存儲四個字節,i>>2表示i/4 一個unsigned int對應4個字節,保存4個字符信息
    }
    strByte[str.length()>>2]|=0x80<<(((str.length()%4))*8);//尾部添加1 一個unsigned int保存4個字符信息,因此用128左移
    /*
    *添加原長度,長度指位的長度,因此要乘8,而後是小端序,因此放在倒數第二個,這裏長度只用了32位
    */
    strByte[num*16-2]=str.length()*8;
    return strByte;
}
string changeHex(int a)
{
    int b;
    string str1;
    string str="";
    for(int i=0;i<4;i++)
    {
        str1="";
        b=((a>>i*8)%(1<<8))&0xff;   //逆序處理每一個字節
        for (int j = 0; j < 2; j++)
        {
            str1.insert(0,1,str16[b%16]);
            b=b/16;
        }
        str+=str1;
    }
    return str;
}
string getMD5(string source)
{
    atemp=A;    //初始化
    btemp=B;
    ctemp=C;
    dtemp=D;
    unsigned int *strByte=add(source);
    for(unsigned int i=0;i<strlength/16;i++)
    {
        unsigned int num[16];
        for(unsigned int j=0;j<16;j++)
            num[j]=strByte[i*16+j];
        mainLoop(num);
    }
    return changeHex(atemp).append(changeHex(btemp)).append(changeHex(ctemp)).append(changeHex(dtemp));
}
unsigned int main()
{
    string ss;
//    cin>>ss;
    string s=getMD5("abc");
    cout<<s;
    return 0;
} java


JAVA實現
參考

 public class MD5{
    /*
    *四個連接變量
    */
    private final int A=0x67452301;
    private final int B=0xefcdab89;
    private final int C=0x98badcfe;
    private final int D=0x10325476;
    /*
    *ABCD的臨時變量
    */
    private int Atemp,Btemp,Ctemp,Dtemp;
     
    /*
    *常量ti
    *公式:floor(abs(sin(i+1))×(2pow32)
    */
    private final int K[]={
        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};
    /*
    *向左位移數,計算方法未知
    */
    private final int s[]={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};
     
     
    /*
    *初始化函數
    */
    private void init(){
        Atemp=A;
        Btemp=B;
        Ctemp=C;
        Dtemp=D;
    }
    /*
    *移動必定位數
    */
    private    int    shift(int a,int s){
        return(a<<s)|(a>>>(32-s));//右移的時候,高位必定要補零,而不是補充符號位
    }
    /*
    *主循環
    */
    private void MainLoop(int M[]){
        int F,g;
        int a=Atemp;
        int b=Btemp;
        int c=Ctemp;
        int d=Dtemp;
        for(int i = 0; i < 64; i ++){
            if(i<16){
                F=(b&c)|((~b)&d);
                g=i;
            }else if(i<32){
                F=(d&b)|((~d)&c);
                g=(5*i+1)%16;
            }else if(i<48){
                F=b^c^d;
                g=(3*i+5)%16;
            }else{
                F=c^(b|(~d));
                g=(7*i)%16;
            }
            int tmp=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;
     
    }
    /*
    *填充函數
    *處理後應知足bits≡448(mod512),字節就是bytes≡56(mode64)
    *填充方式爲先加一個0,其它位補零
    *最後加上64位的原來長度
    */
    private int[] add(String str){
        int num=((str.length()+8)/64)+1;//以512位,64個字節爲一組
        int strByte[]=new int[num*16];//64/4=16,因此有16個整數
        for(int i=0;i<num*16;i++){//所有初始化0
            strByte[i]=0;
        }
        int    i;
        for(i=0;i<str.length();i++){
            strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個字節,小端序
        }
        strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
        /*
        *添加原長度,長度指位的長度,因此要乘8,而後是小端序,因此放在倒數第二個,這裏長度只用了32位
        */
        strByte[num*16-2]=str.length()*8;
            return strByte;
    }
    /*
    *調用函數
    */
    public String getMD5(String source){
        init();
        int strByte[]=add(source);
        for(int i=0;i<strByte.length/16;i++){
        int num[]=new int[16];
        for(int j=0;j<16;j++){
            num[j]=strByte[i*16+j];
        }
        MainLoop(num);
        }
        return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
     
    }
    /*
    *整數變成16進制字符串
    */
    private String changeHex(int a){
        String str="";
        for(int i=0;i<4;i++){
            str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
 
        }
        return str;
    }
    /*
    *單例
    */
    private static MD5 instance;
    public static MD5 getInstance(){
        if(instance==null){
            instance=new MD5();
        }
        return instance;
    }
     
    private MD5(){};
     
    public static void main(String[] args){
        String str=MD5.getInstance().getMD5("");
        System.out.println(str);
    }
}

VB2010實現

Imports System
 
Imports System.Security.Cryptography
 
Imports System.Text
 
 
Module Example
    '哈希輸入字符串並返回一個 32 字符的十六進制字符串哈希。
 
    Function GetMd5Hash(ByVal input As String) As String
 
        '建立新的一個 MD5CryptoServiceProvider 對象的實例。
 
        Dim md5Hasher As New MD5CryptoServiceProvider()
 
        '輸入的字符串轉換爲字節數組,並計算哈希。
 
        Dim data As Byte() = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input))
 
        '建立一個新的 StringBuilder 收集的字節,並建立一個字符串。
 
        Dim sBuilder As New StringBuilder()
 
        '經過每一個字節的哈希數據和格式爲十六進制字符串的每個循環。
 
        For i As Integer = 0 To data.Length - 1
 
            sBuilder.Append(data(i).ToString("x2"))
 
        Next
 
        '返回十六進制字符串。
 
        Return sBuilder.ToString()
 
    End Function
 
 
    '驗證對一個字符串的哈希值。
 
    Function VerifyMd5Hash(ByVal input As String, ByVal hash As String) As Boolean
 
        '哈希的輸入。
 
        Dim hashOfInput As String = GetMd5Hash(input)
 
        '建立 StringComparer 的哈希進行比較。
 
        Dim comparer As StringComparer = StringComparer.OrdinalIgnoreCase
 
        Return comparer.Compare(hashOfInput, hash) = 0
 
    End Function
 
 
    Sub Main()
 
        Dim source As String = "Hello World!"
 
        Dim hash As String = GetMd5Hash(source)
 
        Console.WriteLine($"進行MD5加密的字符串爲:{source},加密的結果是:{hash}。")
 
        Console.WriteLine("正在驗證哈希……")
 
        If VerifyMd5Hash(source, hash) Then
 
            Console.WriteLine("哈希值是
相同的。")
 
Else
 
            Console.WriteLine("哈希值是不相同的。")
 
EndIf
 
    EndSub
 
EndModule
 
 
'此代碼示例產生下面的輸出:
 
 
'進行MD5加密的字符串爲:Hello World!,加密的結果是:ed076287532e86365e841e92bfc50d8c。
 
'正在驗證哈希……
 
'哈希值是相同的。
JavaScript實現

function md5(string) {
    function md5_RotateLeft(lValue, iShiftBits) {
        return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
    }
    function md5_AddUnsigned(lX, lY) {
        var lX4, 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);
        }
    }
    function md5_F(x, y, z) {
        return (x & y) | ((~x) & z);
    }
    function md5_G(x, y, z) {
        return (x & z) | (y & (~z));
    }
    function md5_H(x, y, z) {
        return (x ^ y ^ z);
    }
    function md5_I(x, y, z) {
        return (y ^ (x | (~z)));
    }
    function md5_FF(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_F(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };
    function md5_GG(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_G(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };
    function md5_HH(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_H(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };
    function md5_II(a, b, c, d, x, s, ac) {
        a = md5_AddUnsigned(a, md5_AddUnsigned(md5_AddUnsigned(md5_I(b, c, d), x), ac));
        return md5_AddUnsigned(md5_RotateLeft(a, s), b);
    };
    function md5_ConvertToWordArray(string) {
        var lWordCount;
        var lMessageLength = string.length;
        var lNumberOfWords_temp1 = lMessageLength + 8;
        var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
        var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
        var lWordArray = Array(lNumberOfWords - 1);
        var lBytePosition = 0;
        var lByteCount = 0;
        while (lByteCount < lMessageLength) {
            lWordCount = (lByteCount - (lByteCount % 4)) / 4;
            lBytePosition = (lByteCount % 4) * 8;
            lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount) << lBytePosition));
            lByteCount++;
        }
        lWordCount = (lByteCount - (lByteCount % 4)) / 4;
        lBytePosition = (lByteCount % 4) * 8;
        lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
        lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
        lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
        return lWordArray;
    };
    function md5_WordToHex(lValue) {
        var WordToHexValue = "",
        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);
        }
        return WordToHexValue;
    };
    function md5_Utf8Encode(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";
        for (var n = 0; n < string.length; n++) {
            var c = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((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);
            }
        }
        return utftext;
    };
    var x = Array();
    var k, AA, BB, CC, DD, a, b, c, d;
    var S11 = 7,
    S12 = 12,
    S13 = 17,
    S14 = 22;
    var S21 = 5,
    S22 = 9,
    S23 = 14,
    S24 = 20;
    var S31 = 4,
    S32 = 11,
    S33 = 16,
    S34 = 23;
    var S41 = 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 < x.length; k += 16) {
        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();
}
僞代碼實現
//Note:Allvariablesareunsigned32bitsandwrapmodulo2^32whencalculatingvarint[64]r,k//rspecifiestheper-roundshiftamountsr[0..15]:={7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22}r[16..31]:={5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20}r[32..47]:={4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23}r[48..63]:={6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21}//Usebinaryintegerpartofthesinesofintegersasconstants:forifrom0to63k[i]:=floor(abs(sin(i+1))×2^32)//Initializevariables:varinth0:=0x67452301varinth1:=0xEFCDAB89varinth2:=0x98BADCFEvarinth3:=0x10325476//Pre-processing:append"1"bittomessageappend"0"bitsuntilmessagelengthinbits≡448(mod512)appendbitlengthofmessageas64-bitlittle-endianintegertomessage//Processthemessageinsuccessive512-bitchunks:foreach512-bitchunkofmessagebreakchunkintosixteen32-bitlittle-endianwordsw[i],0≤i≤15//Initializehashvalueforthischunk:varinta:=h0varintb:=h1varintc:=h2varintd:=h3//Mainloop:forifrom0to63if0≤i≤15thenf:=(bandc)or((notb)andd)g:=ielseif16≤i≤31f:=(dandb)or((notd)andc)g:=(5×i+1)mod16elseif32≤i≤47f:=bxorcxordg:=(3×i+5)mod16elseif48≤i≤63f:=cxor(bor(notd))g:=(7×i)mod16temp:=dd:=cc:=bb:=((a+f+k[i]+w[g])leftrotater[i])+ba:=temp//Addthischunk'shashtoresultsofar:h0:=h0+ah1:=h1+bh2:=h2+ch3:=h3+dvarintdigest:=h0appendh1appendh2appendh3//(expressedaslittle-endian)MD5加密工具
利用MD5的算法原理,可使用各類計算機語言進行實現,造成各類各樣的MD5加密校驗工具。有不少的在線工具能夠實現這一點,這些在線工具通常是採用JavaScript語言實現,使用很是方便快捷。[2] 
MD5優點
Van oorschot和Wiener曾經考慮過一個在散列中暴力搜尋衝突的函數(brute-force hash function),並且他們猜想一個被設計專門用來搜索MD5衝突的機器(這臺機器在1994年的制形成本大約是一百萬美圓)能夠平均每24天就找到一個衝突。但單從1991年到2001年這10年間,竟沒有出現替代MD5算法的MD6或被叫作其餘什麼名字的新算法這一點,咱們就能夠看出這個瑕疵並無太多的影響MD5的安全性。上面全部這些都不足以成爲MD5的在實際應用中的問題。而且,因爲MD5算法的使用不須要支付任何版權費用的,因此在通常的狀況下(非絕密應用領域。但即使是應用在絕密領域內,MD5也不失爲一種很是優秀的中間技術),MD5怎麼都應該算得上是很是安全的了。
弱點
編輯
2004年8月17日的美國加州聖巴巴拉的國際密碼學會議(Crypto’2004)上,來自中國山東大學的王小云教授作了破譯MD五、HAVAL-12八、 MD4和RIPEMD算法的報告,公佈了MD系列算法的破解結果。宣告了固若金湯的世界通行密碼標準MD5的堡壘轟然倒塌,引起了密碼學界的軒然大波。(注意:並不是是真正的破解,只是加速了雜湊衝撞)
令世界頂尖密碼學家想象不到的是,破解MD5以後,2005年2月,王小云教授又破解了另外一國際密碼SHA-1。由於SHA-1在美國等國際社會有更加普遍的應用,密碼被破的消息一出,在國際社會的反響可謂石破天驚。換句話說,王小云的研究成果代表了從理論上講電子簽名能夠僞造,必須及時添加限制條件,或者從新選用更爲安全的密碼標準,以保證電子商務的安全。
MD5驗證可執行文件再也不可靠的消息
MD5破解工程權威網站是爲了公開徵集專門針對MD5的攻擊而設立的,網站於2004年8月17日宣佈:「中國研究人員發現了完整MD5算法的碰撞;Wang,Feng,Lai,Yu公佈了MD五、MD四、HAVAL-12八、RIPEMD-128幾個 Hash函數的碰撞。這是近年來密碼學領域最具實質性的研究進展。使用他們的技術,在數個小時內就能夠找到MD5碰撞。……因爲這個里程碑式的發現,MD5CRK項目將在隨後48小時內結束」。
在2004年8月以前,國際密碼學界對王小云這個名字並不熟悉。2004年8月,在美國加州聖芭芭拉召開的國際密碼大會上,並無被安排發言的王小云教授拿着本身的研究成果找到會議主席,沒想到慧眼識珠的會議主席破例給了她15分鐘時間來介紹本身的成果,而一般發言人只被容許有兩三分鐘的時間。王小云與助手展現了MD五、SHA-0及其餘相關雜湊函數的雜湊衝撞。所謂雜湊衝撞指兩個徹底不一樣的訊息經雜湊函數計算得出徹底相同的雜湊值。根據鴿巢原理,以有長度限制的雜湊函數計算沒有長度限制的訊息是必然會有衝撞狀況出現的。但是,一直以來,電腦保安專家都認爲要任意製造出衝撞需時太長,在實際狀況上不可能發生,而王小云等的發現可能會打破這個必然性。就這樣,王小云在國際會議上首次宣佈了她及她的研究小組的研究成果——對MD四、MD五、HAVAL-128和RIPEMD等四個著名密碼算法的破譯結果。
在公佈到第三個成果的時候,會場上已是掌聲四起,報告不得不一度中斷。報告結束後,全部與會專家對他們的突出工做報以長時間的掌聲,有些學者甚至起立鼓掌以示他們的祝賀和敬佩。因爲版本問題,做者在提交會議論文時使用的一組常數和先行標準不一樣,在發現這一問題以後,王小云教授當即改變了那個常數,在很短的時間內就完成了新的數據分析,這段有驚無險的小插曲更證實了他們論文的信服力,攻擊方法的有效性,驗證了研究工做的成功。
2005年8月,王小云、姚期智,以及姚期智妻子姚儲楓(即爲Knuth起名高德納的人)聯手於國際密碼討論年會尾聲部份提出SHA-1雜湊函數雜湊衝撞演算法的改良版。此改良版使破解SHA-1時間縮短。
2006年6月8日,王小云教授於中國科學院第13次院士大會和中國工程院第8次院士大會上以「國際通用Hash函數的破解」獲頒陳嘉庚科學獎信息技術科學獎。
2009年,馮登國、謝濤二人利用差分攻擊,將MD5的碰撞算法複雜度從王小云的2^42進一步下降到2^21,極端狀況下甚至能夠下降至2^10。僅僅2^21的複雜度意味着即使是在2008年的計算機上,也只要幾秒即可以找到一對碰撞。[1] 
空字符串
MD5("")= d41d8cd98f00b204e9800998ecf8427e[3] 
PHP md5() 函數定義和用法
md5() 函數計算字符串的 MD5 散列。[4] 
md5() 函數使用 RSA 數據安全,包括 MD5 報文摘要算法。
來自 RFC 1321 的解釋 - MD5 報文摘要算法:MD5 報文摘要算法將任意長度的信息做爲輸入值,並將其換算成一個 128 位長度的"指紋信息"或"報文摘要"值來表明這個輸入值,並以換算後的值做爲結果。MD5 算法主要是爲數字簽名應用程序而設計的;在這個數字簽名應用程序中,較大的文件將在加密(這裏的加密過程是經過在一個密碼系統下[如:RSA]的公開密鑰下設置私有密鑰而完成的)以前以一種安全的方式進行壓縮。
如需計算文件的 MD5 散列,請使用 md5_file() 函數。
語法
md5(string,raw)
參數描述string必需。規定要計算的字符串。raw可選。規定十六進制或二進制輸出格式:
TRUE - 原始 16 字符二進制格式
FALSE - 默認。32 字符十六進制數
技術細節
返回值:
若是成功則返回已計算的 MD5 散列,若是失敗則返回 FALSE。
PHP 版本:
4+
更新日誌:
在 PHP 5.0 中,raw 參數變成可選的。
實例
輸出 md5() 的結果:
<?php$str="Hello";echo"Thestring:".$str."<br>";echo"TRUE-Raw16characterbinaryformat:".md5($str,TRUE)."<br>";echo"FALSE-32characterhexnumber:".md5($str)."<br>";?>ios

 

oxff介紹:算法

第一,oxff默認爲整形,二進制位最低8位是1111  1111,前面24位都是0;
第二,&運算: 若是2個bit都是1,則得1,不然得0;
第三,byte的8位和0xff進行&運算後,最低8位中,原來爲1的仍是1,原來爲0的仍是0,而0xff其餘位都是0,因此&後仍然得0,
其本質緣由就是想保持二進制補碼的一致性。數據庫

記得在學計算機原理的時候,瞭解到計算機內的存儲都是利用二進制的補碼進行存儲的。
複習一下,原碼反碼補碼這三個概念
對於正數(00000001)原碼來講,首位表示符號位,反碼 補碼都是自己
對於負數(100000001)原碼來講,反碼是對原碼除了符號位以外做取反運算即(111111110),補碼是對反碼做+1運算即(111111111)
概念就這麼簡單。express

當將-127賦值給a[0]時候,a[0]做爲一個byte類型,其計算機存儲的補碼是10000001(8位)。
將a[0] 做爲int類型向控制檯輸出的時候,jvm做了一個補位的處理,由於int類型是32位因此補位後的補碼就是1111111111111111111111111 10000001(32位),這個32位二進制補碼錶示的也是-127.
發現沒有,雖然byte->int計算機背後存儲的二進制補碼由10000001(8位)轉化成了1111111111111111111111111 10000001(32位)很顯然這兩個補碼錶示的十進制數字依然是相同的。
可是我作byte->int的轉化 全部時候都只是爲了保持 十進制的一致性嗎?
不必定吧?比如咱們拿到的文件流轉成byte數組,難道咱們關心的是byte數組的十進制的值是多少嗎?咱們關心的是其背後二進制存儲的補碼吧
因此你們應該能猜到爲何byte類型的數字要&0xff再賦值給int類型,其本質緣由就是想保持二進制補碼的一致性。
當byte要轉化爲int的時候,高的24位必然會補1,這樣,其二進制補碼其實已經不一致了,&0xff能夠將高的24位置爲0,低8位保持原樣。這樣作的目的就是爲了保證二進制數據的一致性。
固然拉,保證了二進制數據性的同時,若是二進制被看成byte和int來解讀,其10進制的值必然是不一樣的,由於符號位位置已經發生了變化。
 
象例2中,int c = a[0]&0xff;  a[0]&0xff=1111111111111111111111111 10000001&11111111=000000000000000000000000 10000001 ,這個值算一下就是129,
因此c的輸出的值就是129。有人問爲何上面的式子中a[0]不是8位而是32位,由於當系統檢測到byte可能會轉化成int或者說byte與int類型進行運算的時候,就會將byte的內存空間高位補1(也就是按符號位補位)擴充到32位,再參與運算。上面的0xff實際上是int類型的字面量值,因此能夠說byte與int進行運算。編程

 

 

一個MD5加密的簡單例子:數組

package zhao;安全

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;服務器

public class MD5 {
public static void main(String[] args) {
/**
* 第一個參數的解釋,記得必定要設置爲1
* signum of the number (-1 for negative, 0 for zero, 1 for positive).
* */
//String yan="asjkca";
//String pwd="123456"+yan;
String pwd="78910";
num(pwd);

}
public static String bytes2hex(byte[]bytes)
{
BigInteger biginteger=new BigInteger(1,bytes);
return biginteger.toString(16);
}
public static void num(String pwd)
{
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
byte[] bs = digest.digest(pwd.getBytes());
StringBuffer stringBuffer = new StringBuffer();
String hexString=bytes2hex(bs);
for(byte b : bs)
{
int i=b & 0xff;//前面有介紹
hexString=Integer.toHexString(i);
// System.out.println(hexString);
if(hexString.length()<2)
{
hexString="0"+hexString;
}
stringBuffer.append(hexString);
System.out.println(hexString);
}

System.out.println(stringBuffer);
System.out.println(stringBuffer.substring(8, 24));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}

}

 

一個對稱加密解密的算法案例:

1. 背景
當前數據庫泄密的事件
數據庫泄密的影響(隱私、商業價值)
數據庫泄密的緣由(未意識到數據的重要性、缺少安全意識、缺少保護措施、)
對稱加密算法
場景
i. 數據存儲,對隱私數據加密保護
ii. 日誌文件,對隱私數據加密保護
iii. 內部系統之間通信
b) 經常使用算法
i. DES、3DES、AES、RC五、TDEAIDEA
非對稱加密算法
場景
i. 雙方系統之間通信,防抵賴
i. 一對多,公鑰能夠多人使用
b) 經常使用算法
RSA、RABIN、揹包算法、ELGAMAL
4.本身動手寫對稱加密算法
a) Java的運算符
I 算數運算符: + - * / % ++ --
Ii 關係運算符:== != > >= < <=
Iii 邏輯運算符: && || !^ & |
Iiii 位運算符:^ & | ~ >> << >>>
b) 算法的原理解析
c) 代碼實現

 

 

package zhao;

import java.math.BigInteger;

public class MD51 {
public static void main(String[] args) throws Exception
{
String pwd="78910";
String key="abc";//密鑰
byte[] srcByte = encoder(pwd,key);
String cipherByte = bytes2hex(srcByte);
System.out.println("加密後的密文");
System.out.println(cipherByte);
byte[] okByte=okcoder(cipherByte,key);
String strByte = bytesString(okByte);
System.out.println("解密後的明文:");
System.out.println(strByte);

}


public static String bytes2hex(byte[] bytes) {
/**
* 第一個參數的解釋,記得必定要設置爲1
* signum of the number (-1 for negative, 0 for zero, 1 for positive).
*/
BigInteger bigInteger = new BigInteger(1, bytes);
return bigInteger.toString(16);
}
public static byte[] encoder(String src,String key)
{
byte[] srcByte = src.getBytes();
byte[] keyByte = key.getBytes();
byte dkey=0;
for(int i=0;i<keyByte.length;i++)
{
dkey ^=keyByte[i];
}
byte x=10;//鹽值
for (int i = 0; i < srcByte.length; i++) {
//System.out.println(srcByte[i]);
srcByte[i]=(byte)(srcByte[i]^dkey^x);
x=srcByte[i];
//System.out.println(srcByte[i]);
}
return srcByte;
}
public static byte[] okcoder(String src,String key)
{
byte[] srcByte = hexString2Bytes(src);
byte[] keyByte = key.getBytes();
byte dkey=0;
for(int i=0;i<keyByte.length;i++)
{
dkey ^=keyByte[i];
}
byte x=10;//鹽值
for (int i =(srcByte.length-1); i >=0 ; i--) {
//System.out.println(srcByte[i]);
if(i!=0)
{
srcByte[i]=(byte)(srcByte[i]^dkey^srcByte[i-1]);
}
else
{
srcByte[i]=(byte)(srcByte[i]^dkey^x);
}
//System.out.println(srcByte[i]);
}
return srcByte;
}
/*
* 字符轉換爲字節
*/
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}

/*
* 16進制字符串轉字節數組
*/
public static byte[] hexString2Bytes(String hex) {

if ((hex == null) || (hex.equals(""))){
return null;
}
else if (hex.length()%2 != 0){
return null;
}
else{
hex = hex.toUpperCase();
int len = hex.length()/2;
byte[] b = new byte[len];
char[] hc = hex.toCharArray();
for (int i=0; i<len; i++){
int p=2*i;
b[i] = (byte) (charToByte(hc[p]) << 4 | charToByte(hc[p+1]));
}
return b;
}

}
public static String bytesString(byte[] b) throws Exception {
String r = new String (b,"UTF-8");
return r;
}

}

相關文章
相關標籤/搜索