MD5

MD5

MD5訊息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函式,可以產生出一個128位(16位元組)的散列值(hash value),用於確保信息傳輸完整一致。MD5由美國密碼學家羅納德·李維斯特(Ronald Linn Rivest)設計,於1992年公開,用以取代MD4算法。

基本介紹

  • 中文名:訊息摘要算法
  • 外文名:Message Digest Algorithm MD5
  • 別稱:摘要算法
  • 提出時間:1991年
  • 套用學科:信息技術,計算機科學
  • 適用領域範圍:軟體下載站、論壇資料庫、系統檔案安全
發展歷史,MD2,MD4,MD5,MD5套用,一致性驗證,數字簽名,安全訪問認證,算法原理,代碼,C++實現,JAVA實現,VB2010實現,JavaScript實現,

發展歷史

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),但除此之外就沒有其他被發現的加密後結果了。

MD5套用

一致性驗證

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實際上一種有損壓縮技術,壓縮前檔案一樣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值進行運算。
圖1.MD5算法的整體流程圖圖1.MD5算法的整體流程圖

代碼

C++實現

#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實現

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 SystemImports System.Security.CryptographyImports System.TextModule 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    EndSubEndModule'此代碼示例產生下面的輸出:'進行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();}

相關詞條

熱門詞條

聯絡我們