MD5的全稱是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由MIT Laboratory for Computer Science和RSA Data Security Inc的Ronald L. Rivest開發出來,經MD2、MD3和MD4發展而來。它的作用是讓大容量信息在用數字簽名軟件簽署私人密匙前被"壓縮"成一種保密的格式(就是把一個任意長度的字節串變換成一定長的大整數)。不管是MD2、MD4還是MD5,它們都需要獲得一個隨機長度的信息并產生一個128位的信息摘要。雖然這些算法的結構或多或少有些相似,但MD2的設計與MD4和MD5完全不同,那是因為MD2是為8位機器做過設計優化的,而MD4和MD5卻是面向32位的電腦。這三個算法的描述和C語言源代碼在Internet RFCs 1321中有詳細的描述(http://www.ietf.org/rfc/rfc1321.txt),這是一份最權威的文檔,由Ronald L. Rivest在1992年8月向IEFT提交。 Rivest在1989年開發出MD2算法。在這個算法中,首先對信息進行數據補位,使信息的字節長度是16的倍數。然后,以一個16位的檢驗和追加到信息末尾。并且根據這個新產生的信息計算出散列值。后來,Rogier和Chauvaud發現如果忽略了檢驗和將產生MD2沖突。MD2算法的加密后結果是唯一的--既沒有重復。 為了加強算法的安全性,Rivest在1990年又開發出MD4算法。MD4算法同樣需要填補信息以確保信息的字節長度加上448后能被512整除(信息字節長度mod 512 = 448)。然后,一個以64位二進制表示的信息的最初長度被添加進來。信息被處理成512位Damg?rd/Merkle迭代結構的區塊,而且每個區塊要通過三個不同步驟的處理。Den Boer和Bosselaers以及其他人很快的發現了攻擊MD4版本中第一步和第三步的漏洞。Dobbertin向大家演示了如何利用一部普通的個人電腦在幾分鐘內找到MD4完整版本中的沖突(這個沖突實際上是一種漏洞,它將導致對不同的內容進行加密卻可能得到相同的加密后結果)。毫無疑問,MD4就此被淘汰掉了。 盡管MD4算法在安全上有個這么大的漏洞,但它對在其后才被開發出來的好幾種信息安全加密算法的出現卻有著不可忽視的引導作用。除了MD5以外,其中比較有名的還有SHA-1、RIPE-MD以及HAVAL等。 一年以后,即1991年,Rivest開發出技術上更為趨近成熟的MD5算法。它在MD4的基礎上增加了"安全-帶子"(Safety-Belts)的概念。雖然MD5比MD4稍微慢一些,但卻更為安全。這個算法很明顯的由四個和MD4設計有少許不同的步驟組成。在MD5算法中,信息-摘要的大小和填充的必要條件與MD4完全相同。Den Boer和Bosselaers曾發現MD5算法中的假沖突(Pseudo-Collisions),但除此之外就沒有其他被發現的加密后結果了。 Van Oorschot和Wiener曾經考慮過一個在散列中暴力搜尋沖突的函數(Brute-Force Hash Function),而且他們猜測一個被設計專門用來搜索MD5沖突的機器(這臺機器在1994年的制造成本大約是一百萬美元)可以平均每24天就找到一個沖突。但單從1991年到2001年這10年間,竟沒有出現替代MD5算法的MD6或被叫做其他什么名字的新算法這一點,我們就可以看出這個瑕疵并沒有太多的影響MD5的安全性。上面所有這些都不足以成為MD5的在實際應用中的問題。并且,由于MD5算法的使用不需要支付任何版權費用的,所以在一般的情況下(非絕密應用領域。但即便是應用在絕密領域內,MD5也不失為一種非常優秀的中間技術),MD5怎么都應該算得上是非常安全的了。 算法的應用 MD5的典型應用是對一段信息(Message)產生信息摘要(Message-Digest),以防止被篡改。比如,在UNIX下有很多軟件在下載的時候都有一個 文件名相同, 文件擴展名為.md5的 文件,在這個 文件中通常只有一行文本,大致結構如: MD5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461 這就是tanajiya.tar.gz 文件的數字簽名。MD5將整個 文件當作一個大文本信息,通過其不可逆的 字符串變換算法,產生了這個唯一的MD5信息摘要。如果在以后傳播這個 文件的過程中,無論 文件的內容發生了任何形式的改變(包括人為修改或者下載過程中線路不穩定引起的傳輸錯誤等),只要你對這個 文件重新計算MD5時就會發現信息摘要不相同,由此可以確定你得到的只是一個不正確的 文件。如果再有一個第三方的 認證機構,用MD5還可以防止 文件作者的"抵賴",這就是所謂的數字簽名應用。 MD5還廣泛用于加密和解密技術上。比如在UNIX 系統中用戶的密碼就是以MD5(或其它類似的算法)經加密后存儲在 文件系統中。當用戶登錄的時候, 系統把用戶輸入的密碼計算成MD5值,然后再去和保存在 文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟, 系統在并不知道用戶密碼的明碼的情況下就可以確定用戶登錄 系統的合法性。這不但可以避免用戶的密碼被具有 系統管理員權限的用戶知道,而且還在一定程度上增加了密碼被破解的難度。 正是因為這個原因,現在被黑客使用最多的一種破譯密碼的方法就是一種被稱為"跑字典"的方法。有兩種方法得到字典,一種是日常搜集的用做密碼的 字符串表,另一種是用排列組合方法生成的,先用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位散列值。 在MD5算法中,首先需要對信息進行填充,使其字節長度對512求余的結果等于448。因此,信息的字節長度(Bits Length)將被擴展至N*512+448,即N*64+56個字節(Bytes),N為一個正整數。填充的方法如下,在信息的后面填充一個1和無數個0,直到滿足上面的條件時才停止用0對信息的填充。然后,在在這個結果后面附加一個以64位二進制表示的填充前信息長度。經過這兩步的處理,現在的信息字節長度=N*512+448+64=(N+1)*512,即長度恰好是512的整數倍。這樣做的原因是為滿足后面處理中對信息長度的要求。 MD5中有四個32位被稱作鏈接變量(Chaining Variable)的整數參數,他們分別為: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)) ?。?amp;是與,|是或,~是非,^是異或) 這四個函數的說明:如果X、Y和Z的對應位是獨立和均勻的,那么結果的每一位也應是獨立和均勻的。 F是一個逐位運算的函數。即,如果X,那么Y,否則Z。函數H是逐位奇偶操作符。 假設Mj表示消息的第j個子分組(從0到15),<< FF(a,b,c,d,Mj,s,ti)表示a=b+((a+(F(b,c,d)+Mj+ti)<< GG(a,b,c,d,Mj,s,ti)表示a=b+((a+(G(b,c,d)+Mj+ti)<< HH(a,b,c,d,Mj,s,ti)表示a=b+((a+(H(b,c,d)+Mj+ti)<< II(a,b,c,d,Mj,s,ti)表示a=b+((a+(I(b,c,d)+Mj+ti)<< 這四輪(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) 常數ti可以如下選擇: 在第i步中,ti是4294967296*abs(sin(i))的整數部分,i的單位是弧度。(4294967296等于2的32次方) 所有這些完成之后,將A、B、C、D分別加上a、b、c、d。然后用下一分組數據繼續運行算法,最后的輸出是A、B、C和D的級聯。 當你按照我上面所說的方法實現MD5算法以后,你可以用以下幾個信息對你做出來的 程序作一個簡單的測試,看看 程序有沒有錯誤。 MD5 ("") = d41d8cd98f00b204e9800998ecf8427e MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661 MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72 MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0 MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f MD5 ("123456789012345678901234567890123456789012345678901234567890123456789 01234567890") = 57edf4a22be3c955ac49da2e2107b67a 如果你用上面的信息分別對你做的MD5算法實例做測試,最后得出的結論和標準答案完全一樣,那我就要在這里象你道一聲祝賀了。要知道,我的 程序在第一次編譯成功的時候是沒有得出和上面相同的結果的。 MD5的安全性 MD5相對MD4所作的改進: 1. 增加了第四輪; 2. 每一步均有唯一的加法常數; 3. 為減弱第二輪中函數G的對稱性從(X&Y)|(X&Z)|(Y&Z)變為(X&Z)|(Y&(~Z)); 4. 第一步加上了上一步的結果,這將引起更快的雪崩效應; 5. 改變了第二輪和第三輪中訪問消息子分組的次序,使其更不相似; 6. 近似優化了每一輪中的循環左移位移量以實現更快的雪崩效應。各輪的位移量互不相同。 ——————————————————————————————————————————————————————— MD5加密算法原理
MD5的Java Bean實現 MD5簡介
MD5的全稱是Message-Digest Algorithm 5,在90年代初由MIT的計算機科學實驗室和RSA Data Security Inc發明,經MD2、MD3和MD4發展而來。
Message-Digest泛指字節串(Message)的Hash變換,就是把一個任意長度的字節串變換成一定長的大整數。請注意我使用了“字節串”而不是“字符串”這個詞,是因為這種變換只與字節的值有關,與字符集或編碼方式無關。
MD5將任意長度的“字節串”變換成一個128bit的大整數,并且它是一個不可逆的字符串變換算法,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串,從數學原理上說,是因為原始的字符串有無窮多個,這有點象不存在反函數的數學函數。
MD5的典型應用是對一段Message(字節串)產生fingerprint(指紋),以防止被“篡改”。舉個例子,你將一段話寫在一個叫readme.txt文件中,并對這個readme.txt產生一個MD5的值并記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內容,你對這個文件重新計算MD5時就會發現。如果再有一個第三方的認證機構,用MD5還可以防止文件作者的“抵賴”,這就是所謂的數字簽名應用。
MD5還廣泛用于加密和解密技術上,在很多操作系統中,用戶的密碼是以MD5值(或類似的其它算法)的方式保存的,用戶Login的時候,系統是把用戶輸入的密碼計算成MD5值,然后再去和系統中保存的MD5值進行比較,而系統并不“知道”用戶的密碼是什么。
一些黑客破獲這種密碼的方法是一種被稱為“跑字典”的方法。有兩種方法得到字典,一種是日常搜集的用做密碼的字符串表,另一種是用排列組合方法生成的,先用MD5程序計算出這些字典項的MD5值,然后再用目標的MD5值在這個字典中檢索。
即使假設密碼的最大長度為8,同時密碼只能是字母和數字,共26+26+10=62個字符,排列組合出的字典的項數則是P(62,1)+P(62,2)….+P(62,8),那也已經是一個很天文的數字了,存儲這個字典就需要TB級的磁盤組,而且這種方法還有一個前提,就是能獲得目標賬戶的密碼MD5值的情況下才可以。
在很多電子商務和社區應用中,管理用戶的Account是一種最常用的基本功能,盡管很多Application Server提供了這些基本組件,但很多應用開發者為了管理的更大的靈活性還是喜歡采用關系數據庫來管理用戶,懶惰的做法是用戶的密碼往往使用明文或簡單的變換后直接保存在數據庫中,因此這些用戶的密碼對軟件開發者或系統管理員來說可以說毫無保密可言,本文的目的是介紹MD5的Java Bean的實現,同時給出用MD5來處理用戶的Account密碼的例子,這種方法使得管理員和程序設計者都無法看到用戶的密碼,盡管他們可以初始化它們。但重要的一點是對于用戶密碼設置習慣的保護。
有興趣的讀者可以從這里取得MD5也就是RFC 1321的文本。http://www.ietf.org/rfc/rfc1321.txt
實現策略
MD5的算法在RFC1321中實際上已經提供了C的實現,我們其實馬上就能想到,至少有兩種用Java實現它的方法,第一種是,用Java語言重新寫整個算法,或者再說簡單點就是把C程序改寫成Java程序。第二種是,用JNI(Java Native Interface)來實現,核心算法仍然用這個C程序,用Java類給它包個殼。
但我個人認為,JNI應該是Java為了解決某類問題時的沒有辦法的辦法(比如與操作系統或I/O設備密切相關的應用),同時為了提供和其它語言的互操作性的一個手段。使用JNI帶來的最大問題是引入了平臺的依賴性,打破了SUN所鼓吹的“一次編寫到處運行”的Java好處。因此,我決定采取第一種方法,一來和大家一起嘗試一下“一次編寫到處運行”的好處,二來檢驗一下Java 2現在對于比較密集的計算的效率問題。
實現過程
限于這篇文章的篇幅,同時也為了更多的讀者能夠真正專注于問題本身,我不想就某一種Java集成開發環境來介紹這個Java Bean的制作過程,介紹一個方法時我發現步驟和命令很清晰,我相信有任何一種Java集成環境三天以上經驗的讀者都會知道如何把這些代碼在集成環境中編譯和運行。用集成環境講述問題往往需要配很多屏幕截圖,這也是我一直對集成環境很頭疼的原因。我使用了一個普通的文本編輯器,同時使用了Sun公司標準的JDK 1.3.0 for Windows NT。
其實把C轉換成Java對于一個有一定C語言基礎的程序員并不困難,這兩個語言的基本語法幾乎完全一致.我大概花了一個小時的時間完成了代碼的轉換工作,我主要作了下面幾件事:
把必須使用的一些#define的宏定義變成Class中的final static,這樣保證在一個進程空間中的多個Instance共享這些數據 刪去了一些無用的#if define,因為我只關心MD5,這個推薦的C實現同時實現了MD2 MD3和 MD4,而且有些#if define還和C不同編譯器有關 將一些計算宏轉換成final static 成員函數。 所有的變量命名與原來C實現中保持一致,在大小寫上作一些符合Java習慣的變化,計算過程中的C函數變成了private方法(成員函數)。 關鍵變量的位長調整 定義了類和方法 需要注意的是,很多早期的C編譯器的int類型是16 bit的,MD5使用了unsigned long int,并認為它是32bit的無符號整數。而在Java中int是32 bit的,long是64 bit的。在MD5的C實現中,使用了大量的位操作。這里需要指出的一點是,盡管Java提供了位操作,由于Java沒有unsigned類型,對于右移位操作多提供了一個無符號右移:>>>,等價于C中的 >> 對于unsigned 數的處理。
因為Java不提供無符號數的運算,兩個大int數相加就會溢出得到一個負數或異常,因此我將一些關鍵變量在Java中改成了long類型(64bit)。我個人認為這比自己去重新定義一組無符號數的類同時重載那些運算符要方便,同時效率高很多并且代碼也易讀,OO(Object Oriented)的濫用反而會導致效率低下。
限于篇幅,這里不再給出原始的C代碼,有興趣對照的讀者朋友可以去看RFC 1321。MD5.java源代碼
測試
在RFC 1321中,給出了Test suite用來檢驗你的實現是否正確:
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
……
這些輸出結果的含義是指:空字符串””的MD5值是d41d8cd98f00b204e9800998ecf8427e,字符串”a”的MD5值是0cc175b9c0f1b6a831c399e269772661…… 編譯并運行我們的程序: javac –d . MD5.java java beartool.MD5 為了將來不與別人的同名程序沖突,我在我的程序的第一行使用了package beartool;
因此編譯命令javac –d . MD5.java 命令在我們的工作目錄下自動建立了一個beartool目錄,目錄下放著編譯成功的 MD5.class
我們將得到和Test suite同樣的結果。當然還可以繼續測試你感興趣的其它MD5變換,例如:
java beartool.MD5 1234
將給出1234的MD5值。
可能是我的計算機知識是從Apple II和Z80單板機開始的,我對大寫十六進制代碼有偏好,如果您想使用小寫的Digest String只需要把byteHEX函數中的A、B、C、D、E、F改成a、b、 c、d、e、f就可以了。
MD5據稱是一種比較耗時的計算,我們的Java版MD5一閃就算出來了,沒遇到什么障礙,而且用肉眼感覺不出來Java版的MD5比C版的慢。
為了測試它的兼容性,我把這個MD5.class文件拷貝到我的另一臺Linux+IBM JDK 1.3的機器上,執行后得到同樣結果,確實是“一次編寫到處運行了”。
Java Bean簡述
現在,我們已經完成并簡單測試了這個Java Class,我們文章的標題是做一個Java Bean。
其實普通的Java Bean很簡單,并不是什么全新的或偉大的概念,就是一個Java的Class,盡管 Sun規定了一些需要實現的方法,但并不是強制的。而EJB(Enterprise Java Bean)無非規定了一些必須實現(非常類似于響應事件)的方法,這些方法是供EJB Container使用(調用)的。
在一個Java Application或Applet里使用這個bean非常簡單,最簡單的方法是你要使用這個類的源碼工作目錄下建一個beartool目錄,把這個class文件拷貝進去,然后在你的程序中import beartool.MD5就可以了。最后打包成.jar或.war是保持這個相對的目錄關系就行了。
Java還有一個小小的好處是你并不需要摘除我們的MD5類中那個main方法,它已經是一個可以工作的Java Bean了。Java有一個非常大的優點是她允許很方便地讓多種運行形式在同一組代碼中共存,比如,你可以寫一個類,它即是一個控制臺Application和GUI Application,同時又是一個Applet,同時還是一個Java Bean,這對于測試、維護和發布程序提供了極大的方便,這里的測試方法main還可以放到一個內部類中,有興趣的讀者可以參考:http://www.cn.ibm.com/developerWorks/java/jw-tips/tip106/index.shtml
這里講述了把測試和示例代碼放在一個內部靜態類的好處,是一種不錯的工程化技巧和途徑。
把Java Bean裝到JSP里
正如我們在本文開頭講述的那樣,我們對這個MD5 Bean的應用是基于一個用戶管理,這里我們假設了一個虛擬社區的用戶login過程,用戶的信息保存在數據庫的個名為users的表中。這個表有兩個字段和我們的這個例子有關,userid :char(20)和pwdmd5 :char(32),userid是這個表的Primary Key,pwdmd5保存密碼的MD5串,MD5值是一個128bit的大整數,表示成16進制的ASCII需要32個字符。
這里給出兩個文件,login.html是用來接受用戶輸入的form,login.jsp用來模擬使用MD5 Bean的login過程。
為了使我們的測試環境簡單起見,我們在JSP中使用了JDK內置的JDBC-ODBC Bridge Driver,community是ODBC的DSN的名字,如果你使用其它的JDBC Driver,替換掉login.jsp中的 Connection con= DriverManager.getConnection("jdbc:odbc:community", "", ""); 即可。
login.jsp的工作原理很簡單,通過post接收用戶輸入的UserID和Password,然后將Password變換成MD5串,然后在users表中尋找UserID和pwdmd5,因為UserID是users表的Primary Key,如果變換后的pwdmd5與表中的記錄不符,那么SQL查詢會得到一個空的結果集。
這里需要簡單介紹的是,使用這個Bean只需要在你的JSP應用程序的WEB-INF/classes下建立一個beartool目錄,然后將MD5.class拷貝到那個目錄下就可以了。如果你使用一些集成開發環境,請參考它們的deploy工具的說明。在JSP使用一個java Bean關鍵的一句聲明是程序中的第2行:
這是所有JSP規范要求JSP容器開發者必須提供的標準Tag。
id=實際上是指示JSP Container創建Bean的實例時用的實例變量名。在后面的之間的Java程序中,你可以引用它。在程序中可以看到,通過 pwdmd5=oMD5.getMD5ofStr (password)引用了我們的MD5 Java Bean提供的唯一一個公共方法: getMD5ofStr。
Java Application Server執行.JSP的過程是先把它預編譯成.java(那些Tag在預編譯時會成為java語句),然后再編譯成.class。這些都是系統自動完成和維護的,那個.class也稱為Servlet。當然,如果你愿意,你也可以幫助Java Application Server去干本該它干的事情,自己直接去寫Servlet,但用Servlet去輸出HTML那簡直是回到了用C寫CGI程序的惡夢時代。
如果你的輸出是一個復雜的表格,比較方便的方法我想還是用一個你所熟悉的HTML編輯器編寫一個“模板”,然后在把JSP代碼“嵌入”進去。盡管這種JSP代碼被有些專家指責為“空心粉”,它的確有個缺點是代碼比較難管理和重復使用,但是程序設計永遠需要的就是這樣的權衡。我個人認為,對于中、小型項目,比較理想的結構是把數據表示(或不嚴格地稱作WEB界面相關)的部分用JSP寫,和界面不相關的放在Bean里面,一般情況下是不需要直接寫Servlet的。
如果你覺得這種方法不是非常的OO(Object Oriented),你可以繼承(extends)它一把,再寫一個bean把用戶管理的功能包進去。
到底能不能兼容?
我測試了三種Java應用服務器環境,Resin 1.2.3、Sun J2EE 1.2、IBM WebSphere 3.5,所幸的是這個Java Bean都沒有任何問題,原因其實是因為它僅僅是個計算程序,不涉及操作系統,I/O設備。其實用其它語言也能簡單地實現它的兼容性的,Java的唯一優點是,你只需提供一個形態的運行碼就可以了。請注意“形態”二字,現在很多計算結構和操作系統除了語言本身之外都定義了大量的代碼形態,很簡單的一段C語言核心代碼,轉換成不同形態要考慮很多問題,使用很多工具,同時受很多限制,有時候學習一種新的“形態”所花費的精力可能比解決問題本身還多。比如光Windows就有EXE、Service、的普通DLL、COM DLL以前還有OCX等等等等,在Unix上雖說要簡單一些,但要也要提供一個.h定義一大堆宏,還要考慮不同平臺編譯器版本的位長度問題。我想這是Java對我來說的一個非常重要的魅力吧。
MD5算法說明
一、補位 二、補數據長度 三、初始化MD5參數 四、處理位操作函數 五、主要變換過程 六、輸出結果
補位: MD5算法先對輸入的數據進行補位,使得數據位長度LEN對512求余的結果是448。即數據擴展至K*512+448位。即K*64+56個字節,K為整數。 具體補位操作:補一個1,然后補0至滿足上述要求。 補數據長度: 用一個64位的數字表示數據的原始長度B,把B用兩個32位數表示。這時,數 據就被填補成長度為512位的倍數。 初始化MD5參數: 四個32位整數 (A,B,C,D) 用來計算信息摘要,初始化使用的是十六進制表 示的數字 A=0X01234567 B=0X89abcdef C=0Xfedcba98 D=0X76543210
處理位操作函數: X,Y,Z為32位整數。 F(X,Y,Z) = X&Y|NOT(X)&Z G(X,Y,Z) = X&Z|Y?(Z) H(X,Y,Z) = X xor Y xor Z I(X,Y,Z) = Y xor (X|not(Z))
主要變換過程: 使用常數組T[1 ... 64], T[i]為32位整數用16進制表示,數據用16個32位 的整數數組M[]表示。 具體過程如下:
/* 處理數據原文 */ For i = 0 to N/16-1 do
/*每一次,把數據原文存放在16個元素的數組X中. */ For j = 0 to 15 do Set X[j] to M[i*16+j]. end /結束對J的循環
/* Save A as AA, B as BB, C as CC, and D as DD. */ AA = A BB = B CC = C DD = D
/* 第1輪*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
/* Do the following 16 operations. */ [ABCD 0 7 1] [DABC 1 12 2] [CDAB 2 17 3] [BCDA 3 22 4] [ABCD 4 7 5] [DABC 5 12 6] [CDAB 6 17 7] [BCDA 7 22 8] [ABCD 8 7 9] [DABC 9 12 10] [CDAB 10 17 11] [BCDA 11 22 12] [ABCD 12 7 13] [DABC 13 12 14] [CDAB 14 17 15] [BCDA 15 22 16]
/* 第2輪* */ /* 以 [abcd k s i]表示如下操作 a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 1 5 17] [DABC 6 9 18] [CDAB 11 14 19] [BCDA 0 20 20] [ABCD 5 5 21] [DABC 10 9 22] [CDAB 15 14 23] [BCDA 4 20 24] [ABCD 9 5 25] [DABC 14 9 26] [CDAB 3 14 27] [BCDA 8 20 28] [ABCD 13 5 29] [DABC 2 9 30] [CDAB 7 14 31] [BCDA 12 20 32]
/* 第3輪*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 5 4 33] [DABC 8 11 34] [CDAB 11 16 35] [BCDA 14 23 36] [ABCD 1 4 37] [DABC 4 11 38] [CDAB 7 16 39] [BCDA 10 23 40] [ABCD 13 4 41] [DABC 0 11 42] [CDAB 3 16 43] [BCDA 6 23 44] [ABCD 9 4 45] [DABC 12 11 46] [CDAB 15 16 47] [BCDA 2 23 48]
/* 第4輪*/ /* 以 [abcd k s i]表示如下操作 a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ /* Do the following 16 operations. */ [ABCD 0 6 49] [DABC 7 10 50] [CDAB 14 15 51] [BCDA 5 21 52] [ABCD 12 6 53] [DABC 3 10 54] [CDAB 10 15 55] [BCDA 1 21 56] [ABCD 8 6 57] [DABC 15 10 58] [CDAB 6 15 59] [BCDA 13 21 60] [ABCD 4 6 61] [DABC 11 10 62] [CDAB 2 15 63] [BCDA 9 21 64]
/* 然后進行如下操作 */ A = A + AA B = B + BB C = C + CC D = D + DD
end /* 結束對I的循環*/
輸出結果。
—————————————————————————————————————————————————————— 一、算法實現 1、MD5算法是對輸入的數據進行補位,使得如果數據位長度LEN對512求余的結果? 是448。? ???即數據擴展至K*512+448位。即K*64+56個字節,K為整數。? ???具體補位操作:補一個1,然后補0至滿足上述要求??? 2、補數據長度:? ???用一個64位的數字表示數據的原始長度B,把B用兩個32位數表示。這時,數據? 就被填? ???補成長度為512位的倍數。? 3.?初始化MD5參數??? ???四個32位整數?(A,B,C,D)?用來計算信息摘要,初始化使用的是十六進制表示? 的數字? ??????A=0X01234567? ??????B=0X89abcdef? ??????C=0Xfedcba98? ??????D=0X76543210??? 4、處理位操作函數??? ??????X,Y,Z為32位整數。? ??????F(X,Y,Z)?=?X&Y|NOT(X)&Z? ??????G(X,Y,Z)?=?X&Z|Y?(Z)? ??????H(X,Y,Z)?=?X?xor?Y?xor?Z? ??????I(X,Y,Z)?=?Y?xor?(X|not(Z))??? 5、主要變換過程:? ???使用常數組T[1?...?64],?T[i]為32位整數用16進制表示,數據用16個32位的? 整? ???數數組M[]表示。? ???具體過程如下:??? /*?處理數據原文?*/? For?i?=?0?to?N/16-1?do??? /*每一次,把數據原文存放在16個元素的數組X中.?*/? For?j?=?0?to?15?do? Set?X[j]?to?M[i*16+j].? end??/結束對J的循環? /*?Save?A?as?AA,?B?as?BB,?C?as?CC,?and?D?as?DD.?*/? AA?=?A? BB?=?B? CC?=?C? DD?=?D??? /*?第1輪*/? /*?以?[abcd?k?s?i]表示如下操作? a?=?b?+?((a?+?F(b,c,d)?+?X[k]?+?T[i])?<<<?s).?*/? /*?Do?the?following?16?operations.?*/? [ABCD?0?7?1]?[DABC?1?12?2]?[CDAB?2?17?3]?[BCDA?3?22?4]? [ABCD?4?7?5]?[DABC?5?12?6]?[CDAB?6?17?7]?[BCDA?7?22?8]? [ABCD?8?7?9]?[DABC?9?12?10]?[CDAB?10?17?11]?[BCDA?11?22?12]? [ABCD?12?7?13]?[DABC?13?12?14]?[CDAB?14?17?15]?[BCDA?15?22?16]??? /*?第2輪*?*/? /*?以?[abcd?k?s?i]表示如下操作? a?=?b?+?((a?+?G(b,c,d)?+?X[k]?+?T[i])?<<<?s).?*/? /*?Do?the?following?16?operations.?*/? [ABCD?1?5?17]?[DABC?6?9?18]?[CDAB?11?14?19]?[BCDA?0?20?20]? [ABCD?5?5?21]?[DABC?10?9?22]?[CDAB?15?14?23]?[BCDA?4?20?24]? [ABCD?9?5?25]?[DABC?14?9?26]?[CDAB?3?14?27]?[BCDA?8?20?28]? [ABCD?13?5?29]?[DABC?2?9?30]?[CDAB?7?14?31]?[BCDA?12?20?32]??? /*?第3輪*/? /*?以?[abcd?k?s?i]表示如下操作? a?=?b?+?((a?+?H(b,c,d)?+?X[k]?+?T[i])?<<<?s).?*/? /*?Do?the?following?16?operations.?*/? [ABCD?5?4?33]?[DABC?8?11?34]?[CDAB?11?16?35]?[BCDA?14?23?36]? [ABCD?1?4?37]?[DABC?4?11?38]?[CDAB?7?16?39]?[BCDA?10?23?40]? [ABCD?13?4?41]?[DABC?0?11?42]?[CDAB?3?16?43]?[BCDA?6?23?44]? [ABCD?9?4?45]?[DABC?12?11?46]?[CDAB?15?16?47]?[BCDA?2?23?48]??? /*?第4輪*/? /*?以?[abcd?k?s?i]表示如下操作? a?=?b?+?((a?+?I(b,c,d)?+?X[k]?+?T[i])?<<<?s).?*/? /*?Do?the?following?16?operations.?*/? [ABCD?0?6?49]?[DABC?7?10?50]?[CDAB?14?15?51]?[BCDA?5?21?52]? [ABCD?12?6?53]?[DABC?3?10?54]?[CDAB?10?15?55]?[BCDA?1?21?56]? [ABCD?8?6?57]?[DABC?15?10?58]?[CDAB?6?15?59]?[BCDA?13?21?60]? [ABCD?4?6?61]?[DABC?11?10?62]?[CDAB?2?15?63]?[BCDA?9?21?64]??? /*?然后進行如下操作?*/? A?=?A?+?AA? B?=?B?+?BB? C?=?C?+?CC? D?=?D?+?DD??? end?/*?結束對I的循環*/? 6、輸出結果
——————————————————————————————————————————————————————
md5算法的java源代碼
public class MD5 { /* ? * A Java implementation of the RSA Data Security, Inc. MD5 Message ? * Digest Algorithm, as defined in RFC 1321. ? * Based on the JavaScript implementation of Paul Johnston ? * ? ? Copyright (C) Paul Johnston 1999 - 2000. ? * ? ? See http://pajhome.org.uk/site/legal.html for details. ? * Java Version by Thomas Weber (Orange Interactive GmbH) ? */
/* ? * Convert a 32-bit number to a hex string with ls-byte first ? */ String hex_chr = "0123456789abcdef"; private String rhex(int num) { ? String str = ""; ? for(int j = 0; j <= 3; j++) ? ? str = str + hex_chr.charAt((num >;>; (j * 8 + 4)) & 0x0F) + hex_chr.charAt((num >;>; (j * 8)) & 0x0F); ? return str; }
/* ? * Convert a string to a sequence of 16-word blocks, stored as an array. ? * Append padding bits and the length, as described in the MD5 standard. ? */ private int[] str2blks_MD5(String str) { ? int nblk = ((str.length() + 8) >;>; 6) + 1; ? int[] blks = new int[nblk * 16]; ? int i = 0; ? for(i = 0; i < nblk * 16; i++) { ? ? blks = 0; ? } ? for(i = 0; i < str.length(); i++) { ? ? blks[i >;>; 2] |= str.charAt(i) << ((i % 4) * 8); ? } ? blks[i >;>; 2] |= 0x80 << ((i % 4) * 8); ? blks[nblk * 16 - 2] = str.length()*8; ? ? ? return blks; }
/* ? * Add integers, wrapping at 2^32 ? */ private int add(int x, int y) { ? return ((x&0x7FFFFFFF) + (y&0x7FFFFFFF)) ^ (x&0x80000000) ^ (y&0x80000000); }
/* ? * Bitwise rotate a 32-bit number to the left ? */ private int rol(int num, int cnt) { ? return (num << cnt) | (num >;>;>; (32 - cnt)); }
/* ? * These functions implement the basic operation for each round of the ? * algorithm. ? */ private int cmn(int q, int a, int b, int x, int s, int t) { ? return add(rol(add(add(a, q), add(x, t)), s), b); } private int ff(int a, int b, int c, int d, int x, int s, int t) { ? return cmn((b & c) | ((~b) & d), a, b, x, s, t); } private int gg(int a, int b, int c, int d, int x, int s, int t) { ? return cmn((b & d) | (c & (~d)), a, b, x, s, t); } private int hh(int a, int b, int c, int d, int x, int s, int t) { ? return cmn(b ^ c ^ d, a, b, x, s, t); } private int ii(int a, int b, int c, int d, int x, int s, int t) { ? return cmn(c ^ (b | (~d)), a, b, x, s, t); }
/* ? * Take a string and return the hex representation of its MD5. ? */ public String calcMD5(String str) { ? int[] x = str2blks_MD5(str); ? int a = 0x67452301; ? int b = 0xEFCDAB89; ? int c = 0x98BADCFE; ? int d = 0x10325476;
? for(int i = 0; i < x.length; i += 16) ? { ? ? int olda = a; ? ? int oldb = b; ? ? int oldc = c; ? ? int oldd = d;
? ? a = ff(a, b, c, d, x[i+ 0], 7 , 0xD76AA478); ? ? d = ff(d, a, b, c, x[i+ 1], 12, 0xE8C7B756); ? ? c = ff(c, d, a, b, x[i+ 2], 17, 0x242070DB); ? ? b = ff(b, c, d, a, x[i+ 3], 22, 0xC1BDCEEE); ? ? a = ff(a, b, c, d, x[i+ 4], 7 , 0xF57C0FAF); ? ? d = ff(d, a, b, c, x[i+ 5], 12, 0x4787C62A); ? ? c = ff(c, d, a, b, x[i+ 6], 17, 0xA8304613); ? ? b = ff(b, c, d, a, x[i+ 7], 22, 0xFD469501); ? ? a = ff(a, b, c, d, x[i+ 8], 7 , 0x698098D8); ? ? d = ff(d, a, b, c, x[i+ 9], 12, 0x8B44F7AF); ? ? c = ff(c, d, a, b, x[i+10], 17, 0xFFFF5BB1); ? ? b = ff(b, c, d, a, x[i+11], 22, 0x895CD7BE); ? ? a = ff(a, b, c, d, x[i+12], 7 , 0x6B901122); ? ? d = ff(d, a, b, c, x[i+13], 12, 0xFD987193); ? ? c = ff(c, d, a, b, x[i+14], 17, 0xA679438E); ? ? b = ff(b, c, d, a, x[i+15], 22, 0x49B40821);
? ? a = gg(a, b, c, d, x[i+ 1], 5 , 0xF61E2562); ? ? d = gg(d, a, b, c, x[i+ 6], 9 , 0xC040B340); ? ? c = gg(c, d, a, b, x[i+11], 14, 0x265E5A51); ? ? b = gg(b, c, d, a, x[i+ 0], 20, 0xE9B6C7AA); ? ? a = gg(a, b, c, d, x[i+ 5], 5 , 0xD62F105D); ? ? d = gg(d, a, b, c, x[i+10], 9 , 0x02441453); ? ? c = gg(c, d, a, b, x[i+15], 14, 0xD8A1E681); ? ? b = gg(b, c, d, a, x[i+ 4], 20, 0xE7D3FBC8); ? ? a = gg(a, b, c, d, x[i+ 9], 5 , 0x21E1CDE6); ? ? d = gg(d, a, b, c, x[i+14], 9 , 0xC33707D6); ? ? c = gg(c, d, a, b, x[i+ 3], 14, 0xF4D50D87); ? ? b = gg(b, c, d, a, x[i+ 8], 20, 0x455A14ED); ? ? a = gg(a, b, c, d, x[i+13], 5 , 0xA9E3E905); ? ? d = gg(d, a, b, c, x[i+ 2], 9 , 0xFCEFA3F8); ? ? c = gg(c, d, a, b, x[i+ 7], 14, 0x676F02D9); ? ? b = gg(b, c, d, a, x[i+12], 20, 0x8D2A4C8A);
? ? a = hh(a, b, c, d, x[i+ 5], 4 , 0xFFFA3942); ? ? d = hh(d, a, b, c, x[i+ 8], 11, 0x8771F681); ? ? c = hh(c, d, a, b, x[i+11], 16, 0x6D9D6122); ? ? b = hh(b, c, d, a, x[i+14], 23, 0xFDE5380C); ? ? a = hh(a, b, c, d, x[i+ 1], 4 , 0xA4BEEA44); ? ? d = hh(d, a, b, c, x[i+ 4], 11, 0x4BDECFA9); ? ? c = hh(c, d, a, b, x[i+ 7], 16, 0xF6BB4B60); ? ? b = hh(b, c, d, a, x[i+10], 23, 0xBEBFBC70); ? ? a = hh(a, b, c, d, x[i+13], 4 , 0x289B7EC6); ? ? d = hh(d, a, b, c, x[i+ 0], 11, 0xEAA127FA); ? ? c = hh(c, d, a, b, x[i+ 3], 16, 0xD4EF3085); ? ? b = hh(b, c, d, a, x[i+ 6], 23, 0x04881D05); ? ? a = hh(a, b, c, d, x[i+ 9], 4 , 0xD9D4D039); ? ? d = hh(d, a, b, c, x[i+12], 11, 0xE6DB99E5); ? ? c = hh(c, d, a, b, x[i+15], 16, 0x1FA27CF8); ? ? b = hh(b, c, d, a, x[i+ 2], 23, 0xC4AC5665);
? ? a = ii(a, b, c, d, x[i+ 0], 6 , 0xF4292244); ? ? d = ii(d, a, b, c, x[i+ 7], 10, 0x432AFF97); ? ? c = ii(c, d, a, b, x[i+14], 15, 0xAB9423A7); ? ? b = ii(b, c, d, a, x[i+ 5], 21, 0xFC93A039); ? ? a = ii(a, b, c, d, x[i+12], 6 , 0x655B59C3); ? ? d = ii(d, a, b, c, x[i+ 3], 10, 0x8F0CCC92); ? ? c = ii(c, d, a, b, x[i+10], 15, 0xFFEFF47D); ? ? b = ii(b, c, d, a, x[i+ 1], 21, 0x85845DD1); ? ? a = ii(a, b, c, d, x[i+ 8], 6 , 0x6FA87E4F); ? ? d = ii(d, a, b, c, x[i+15], 10, 0xFE2CE6E0); ? ? c = ii(c, d, a, b, x[i+ 6], 15, 0xA3014314); ? ? b = ii(b, c, d, a, x[i+13], 21, 0x4E0811A1); ? ? a = ii(a, b, c, d, x[i+ 4], 6 , 0xF7537E82); ? ? d = ii(d, a, b, c, x[i+11], 10, 0xBD3AF235); ? ? c = ii(c, d, a, b, x[i+ 2], 15, 0x2AD7D2BB); ? ? b = ii(b, c, d, a, x[i+ 9], 21, 0xEB86D391);
? ? a = add(a, olda); ? ? b = add(b, oldb); ? ? c = add(c, oldc); ? ? d = add(d, oldd); ? } ? return rhex(a) + rhex(b) + rhex(c) + rhex(d); }
}
|