MD5摘要算法簡析


1 MD5簡介

1.1  概述

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

1.2  發展歷史

MD2

  Rivest在1989年開發出MD2算法。在這個算法中,首先對信息進行數據補位,使信息的字節長度是16的倍數。然后,以一個16位的檢驗和追加到信息末尾,並且根據這個新產生的信息計算出散列值。后來,Rogier和Chauvaud發現如果忽略了檢驗將和MD2產生沖突。MD2算法 加密后結果是唯一的(即不同信息加密后的結果不同)。

MD4

  為了加強算法的安全性,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),但除此之外就沒有其他被發現的加密后結果了。

1.3 MD5算法原理

  對於MD5算法可以簡要的概括為:MD5是以512位分組來處理輸入信息,且每一分組又被划分為16個32位子分組,經過了一系列的處理后,算法的輸出由四個32位分組組成,將這四個32位分組級聯后將生成一個128位散列值。

  總體流程圖如下:

  

 

1.3.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的整數倍。這樣做的原因是為滿足后面處理中對信息長度的要求。

1.3.2 初始化變量

  初始的128位值為初試鏈接變量,這些參數用於第一輪的運算,以大端 字節序來表示,他們分別為: A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。
(每一個變量給出的數值是高字節存於內存低地址,低字節存於內存高地址,即大端字節序。在程序中變量A、B、C、D的值分別為0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476)

1.3.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=2 32)
  現定義:
  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
  然后用下一分組數據繼續運行以上算法。

1.3.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

1.4 MD5應用

1.4.1 一致性驗證

  MD5的典型應用是對一段信息(Message)產生信息摘要(Message-Digest),以防止被篡改。比如,在 Unix下有很多軟件在下載的時候都有一個文件名相同,文件擴展名為.md5的文件,在這個文件中通常只有一行文本,大致結構如:
  MD5 (tanajiya.tar.gz) = 38b8c2c1093dd0fec383a9d9ac940515
  這就是tanajiya.tar.gz文件的數字簽名。MD5將整個文件當作一個大文本信息,通過其不可逆的字符串變換算法,產生了這個唯一的MD5信息摘要。為了讓讀者朋友對MD5的應用有個直觀的認識,筆者以一個比方和一個實例來簡要描述一下其工作過程:  

  大家都知道,地球上任何人都有自己獨一無二的指紋,這常常成為司法機關鑒別罪犯身份最值得信賴的方法;與之類似,MD5就可以為任何文件(不管其大小、格式、數量)產生一個同樣獨一無二的“數字指紋”,如果任何人對文件做了任何改動,其MD5值也就是對應的“數字指紋”都會發生變化。

  我們常常在某些軟件下載站點的某軟件信息中看到其MD5值,它的作用就在於我們可以在下載該軟件后,對下載回來的文件用專門的軟件(如Windows MD5 Check等)做一次 MD5校驗,以確保我們獲得的文件與該站點提供的文件為同一文件。
  具體來說文件的MD5值就像是這個文件的“ 數字指紋”。每個文件的MD5值是不同的,如果任何人對文件做了任何改動,其 MD5值也就是對應的“數字指紋”就會發生變化。比如下載服務器針對一個文件預先提供一個MD5值,用戶下載完該文件后,用我這個算法重新計算下載文件的MD5值,通過比較這兩個值是否相同,就能判斷下載的文件是否出錯,或者說下載的文件是否被篡改了。
  利用MD5算法來進行文件校驗的方案被大量應用到軟件下載站、論壇數據庫、系統文件安全等方面。

1.4.2 數字簽名

  MD5的典型應用是對一段Message(字節串)產生fingerprint(指紋),以防止被“篡改”。舉個例子,你將一段話寫在一個叫 readme.txt文件中,並對這個readme.txt產生一個MD5的值並記錄在案,然后你可以傳播這個文件給別人,別人如果修改了文件中的任何內容,你對這個文件重新計算MD5時就會發現(兩個MD5值不相同)。如果再有一個第三方的認證機構,用MD5還可以防止文件作者的“抵賴”,這就是所謂的數字簽名應用。

1.4.3 安全訪問認證

  MD5還廣泛用於操作系統的登陸認證上,如Unix、各類BSD系統登錄密碼、數字簽名等諸多方面。如在Unix系統中用戶的密碼是以MD5(或其它類似的算法)經Hash運算后存儲在文件系統中。當用戶登錄的時候,系統把用戶輸入的密碼進行MD5 Hash運算,然后再去和保存在文件系統中的MD5值進行比較,進而確定輸入的密碼是否正確。通過這樣的步驟,系統在並不知道用戶密碼的明碼的情況下就可以確定用戶登錄系統的合法性。這可以避免用戶的密碼被具有系統管理員權限的用戶知道。MD5將任意長度的“字節串”映射為一個128bit的大整數,並且是通過該128bit反推原始字符串是困難的,換句話說就是,即使你看到源程序和算法描述,也無法將一個MD5的值變換回原始的字符串,從數學原理上說,是因為原始的字符串有無窮多個,這有點象不存在反函數的數學函數。所以,要遇到了md5密碼的問題,比較好的辦法是:你可以用這個系統中的md5()函數重新設一個密碼,如admin,把生成的一串密碼的Hash值覆蓋原來的Hash值就行了。

1.5 MD5特點

  MD5算法具有以下特點:
  1、壓縮性:任意長度的數據,算出的MD5值長度都是固定的。
  2、容易計算:從原數據計算出MD5值很容易。
  3、抗修改性:對原數據進行任何改動,哪怕只修改1個字節,所得到的MD5值都有很大區別。
  4、強抗碰撞:已知原數據和其MD5值,想找到一個具有相同MD5值的數據(即偽造數據)是非常困難的。
  MD5的作用是讓大容量信息在用 數字簽名軟件簽署私人 密鑰前被" 壓縮"成一種保密的格式(就是把一個任意長度的字節串變換成一定長的 十六進制數字串)。除了MD5以外,其中比較有名的還有 sha-1RIPEMD以及Haval等。

2 java代碼實現MD5摘要算法

2.1 MD5詳細實現代碼

  1 package xin.dreaming.md5;
  2 
  3 public class MD5 {
  4     /*
  5         *四個鏈接變量
  6         */
  7         private final int A=0x67452301;
  8         private final int B=0xefcdab89;
  9         private final int C=0x98badcfe;
 10         private final int D=0x10325476;
 11         /*
 12         *ABCD的臨時變量
 13         */
 14         private int Atemp,Btemp,Ctemp,Dtemp;
 15          
 16         /*
 17         *常量ti
 18         *公式:floor(abs(sin(i+1))×(2pow32)
 19         */
 20         private final int K[]={
 21             0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
 22             0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
 23             0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
 24             0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
 25             0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
 26             0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
 27             0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
 28             0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
 29             0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
 30             0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
 31             0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
 32             0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
 33             0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
 34         /*
 35         *向左位移數,計算方法未知
 36         */
 37         private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
 38             12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
 39             4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
 40             15,21,6,10,15,21,6,10,15,21,6,10,15,21};
 41          
 42          
 43         /*
 44         *初始化函數
 45         */
 46         private void init(){
 47             Atemp=A;
 48             Btemp=B;
 49             Ctemp=C;
 50             Dtemp=D;
 51         }
 52         /*
 53         *移動一定位數
 54         */
 55         private    int    shift(int a,int s){
 56             return(a<<s)|(a>>>(32-s));//右移的時候,高位一定要補零,而不是補充符號位
 57         }
 58         /*
 59         *主循環
 60         */
 61         private void MainLoop(int M[]){
 62             int F,g;
 63             int a=Atemp;
 64             int b=Btemp;
 65             int c=Ctemp;
 66             int d=Dtemp;
 67             for(int i = 0; i < 64; i ++){
 68                 if(i<16){
 69                     F=(b&c)|((~b)&d);
 70                     g=i;
 71                 }else if(i<32){
 72                     F=(d&b)|((~d)&c);
 73                     g=(5*i+1)%16;
 74                 }else if(i<48){
 75                     F=b^c^d;
 76                     g=(3*i+5)%16;
 77                 }else{
 78                     F=c^(b|(~d));
 79                     g=(7*i)%16;
 80                 }
 81                 int tmp=d;
 82                 d=c;
 83                 c=b;
 84                 b=b+shift(a+F+K[i]+M[g],s[i]);
 85                 a=tmp;
 86             }
 87             Atemp=a+Atemp;
 88             Btemp=b+Btemp;
 89             Ctemp=c+Ctemp;
 90             Dtemp=d+Dtemp;
 91          
 92         }
 93         /*
 94         *填充函數
 95         *處理后應滿足bits≡448(mod512),字節就是bytes≡56(mode64)
 96         *填充方式為先加一個0,其它位補零
 97         *最后加上64位的原來長度
 98         */
 99         private int[] add(String str){
100             int num=((str.length()+8)/64)+1;//以512位,64個字節為一組
101             int strByte[]=new int[num*16];//64/4=16,所以有16個整數
102             for(int i=0;i<num*16;i++){//全部初始化0
103                 strByte[i]=0;
104             }
105             int    i;
106             for(i=0;i<str.length();i++){
107                 strByte[i>>2]|=str.charAt(i)<<((i%4)*8);//一個整數存儲四個字節,小端序
108             }
109             strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
110             /*
111             *添加原長度,長度指位的長度,所以要乘8,然后是小端序,所以放在倒數第二個,這里長度只用了32位
112             */
113             strByte[num*16-2]=str.length()*8;
114                 return strByte;
115         }
116         /*
117         *調用函數
118         */
119         public String getMD5(String source){
120             init();
121             int strByte[]=add(source);
122             for(int i=0;i<strByte.length/16;i++){
123             int num[]=new int[16];
124             for(int j=0;j<16;j++){
125                 num[j]=strByte[i*16+j];
126             }
127             MainLoop(num);
128             }
129             return changeHex(Atemp)+changeHex(Btemp)+changeHex(Ctemp)+changeHex(Dtemp);
130          
131         }
132         /*
133         *整數變成16進制字符串
134         */
135         private String changeHex(int a){
136             String str="";
137             for(int i=0;i<4;i++){
138                 str+=String.format("%2s", Integer.toHexString(((a>>i*8)%(1<<8))&0xff)).replace(' ', '0');
139      
140             }
141             return str;
142         }
143         /*
144         *單例
145         */
146         private static MD5 instance;
147         public static MD5 getInstance(){
148             if(instance==null){
149                 instance=new MD5();
150             }
151             return instance;
152         }
153          
154         private MD5(){};
155          
156         public static void main(String[] args){
157             String str=MD5.getInstance().getMD5("123");
158             System.out.println(str);
159         }
160 }

  代碼摘要結果:

  

  此處代碼可以參考前邊MD5原理流程,了解其代碼執行流程。

2.2 使用java.security.MessageDigest實現MD5摘要算法

2.2.1代碼實現:

 1 /**
 2      * md5計算.
 3      * 
 4      * @param datas
 5      *            待計算的數據
 6      * @return 計算結果
 7      */
 8     public static byte[] md5(byte[] datas) {
 9         MessageDigest md = null;
10         try {
11             md = MessageDigest.getInstance("MD5");
12             md.reset();
13             md.update(datas);
14             return md.digest();
15         } catch (Exception e) {
16             LogUtil.writeErrorLog("MD5計算失敗", e);
17             return null;
18         }
19     }

2.2.2  MessageDigest 簡要說明

  java.security. MessageDigest 類用於為應用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。簡單點說就是用於生成散列碼。 信息摘要是安全的單向哈希函數,它接收任意大小的數據,輸出固定長度的哈希值。關於信息摘要和散列碼請參照《 數字證書簡介

  MessageDigest 通過其getInstance系列靜態函數來進行實例化和初始化。MessageDigest 對象通過使用 update 方法處理數據。任何時候都可以調用 reset 方法重置摘要。一旦所有需要更新的數據都已經被更新了,應該調用 digest 方法之一完成哈希計算並返回結果。

  對於給定數量的更新數據,digest 方法只能被調用一次。digest 方法被調用后,MessageDigest  對象被重新設置成其初始狀態。

  MessageDigest 的實現可隨意選擇是否實現 Cloneable 接口。客戶端應用程可以通過嘗試復制和捕獲 CloneNotSupportedException 測試可復制性:

 

參考:

  1、https://baike.baidu.com/item/MD5/212708?fr=aladdin#1_3

  2、http://hubingforever.blog.163.com/blog/static/171040579201210781650340/

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM