MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函式,可以產生出一個128位(16位元組)的散列值(hash value),用於確保信息傳輸完整一致。MD5由美國密碼學家羅納德·李維斯特(Ronald Linn Rivest)設計,於1992年公開,用以取代MD4算法。這套算法的程式在 RFC 1321 標準中被加以規範。1996年後該算法被證實存在弱點,可以被加以破解,對於需要高度安全性的數據,專家一般建議改用其他算法,如SHA-2。2004年,證實MD5算法無法防止碰撞(collision),因此不適用於安全性認證,如SSL公開密鑰認證或是數字簽名等用途。
基本介紹
- 中文名:訊息摘要算法
- 外文名:Message Digest Algorithm MD5
- 別名:摘要算法
- 提出者:羅納德·李維斯特
- 提出時間:1992年
- 套用學科:信息技術,計算機科學
發展歷史
MD2
MD4
MD5
原理
算法步驟
按位補充數據
擴展長度
初始化MD快取器
處理數據段
輸出
套用
用於密碼管理
電子簽名
垃圾郵件篩選
- 建立一個郵件 MD5 值資料庫,分別儲存郵件的 MD5 值、允許出現的次數(假定為 3)和出現次數(初值為零)。
- 對每一封收到的郵件,將它的正文部分進行MD5 計算,得到 MD5 值,將這個值在資料庫中進行搜尋。
- 如未發現相同的 MD5 值,說明此郵件是第一次收到,將此 MD5 值存入資料庫,並將出現次數置為1,轉到第五步。
- 如發現相同的 MD5 值,說明收到過同樣內容的郵件,將出現次數加 1,並與允許出現次數相比較,如小於允許出現次數,就轉到第五步。否則中止接收該郵件。結束。
- 接收該郵件。
檔案完整性校驗
安全性分析
- 增加了第四輪。
- 每一步均有唯一的加法常數。
- 減弱第二輪中函式的對稱性。
- 第一步加上了上一步的結果,這將引起更快的雪崩效應(就是對明文或者密鑰改變 1bit 都會引起密文的巨大不同)。
- 改變了第二輪和第三輪中訪問訊息子分組的次序,使其更不相似。
- 近似最佳化了每一輪中的循環左移位移量以實現更快的雪崩效應,各輪的位移量互不相同。
代碼
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 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();
}