MD5(单向散列算法)
的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2、MD3和MD4发展而来。MD5算法的使用不需要支付任何版权费用。
MD5功能:
输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
不同的输入得到的不同的结果(唯一性);
根据128位的输出结果不可能反推出输入的信息(不可逆);
MD5属不属于加密算法:
认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有解密算法,所以这部分人认为MD5只能属于算法,不能称为加密算法;
认为属于的人是因为他们觉得经过MD5处理后看不到原文,即已经将原文加密,所以认为MD5属于加密算法;我个人支持后者。
MD5用途:
1、 防止被篡改 :
1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.
2、 防止直接看到明文 :
现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)
3、 防止抵赖(数字签名):
这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。
MD5算法过程:
对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
第一步、填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);
第二步、记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N*512+448+64=(N+1)*512位。
第三步、装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567) 16 ,B=(89ABCDEF) 16 ,C=(FEDCBA98) 16 ,D=(76543210) 16 )。如果在程序中定义应该是(A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点晕哈,其实想一想就明白了。
第四步、四轮循环运算:循环的次数是分组的个数(N+1)
1)将每一512字节细分成16个小组,每个小组64位(8个字节)
2)先认识四个线性函数(&是与,|是或,~是非,^是异或)
3)设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s位,则四种操作为:
4)四轮运算
5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环。
如果上面的过程用JAVA代码来实现的话,代码如下:
package
woxingwosu;


/************************************************
*MD5算法
*@author我行我素
*@Date2007-07-01
*************************************************/
public
class
MD5
{

staticfinalStringhexs[]={"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"};
//标准的幻数
privatestaticfinallongA=0x67452301L;
privatestaticfinallongB=0xefcdab89L;
privatestaticfinallongC=0x98badcfeL;
privatestaticfinallongD=0x10325476L;


//下面这些S11-S44实际上是一个4*4的矩阵,在四轮循环运算中用到
staticfinalintS11=7;
staticfinalintS12=12;
staticfinalintS13=17;
staticfinalintS14=22;

staticfinalintS21=5;
staticfinalintS22=9;
staticfinalintS23=14;
staticfinalintS24=20;

staticfinalintS31=4;
staticfinalintS32=11;
staticfinalintS33=16;
staticfinalintS34=23;

staticfinalintS41=6;
staticfinalintS42=10;
staticfinalintS43=15;
staticfinalintS44=21;

//java不支持无符号的基本数据(unsigned)
privatelong[]result={A,B,C,D};//存储hash结果,共4×32=128位,初始化值为(幻数的级联)

publicstaticvoidmain(String[]args){
MD5md=newMD5();
System.out.println("md5(abc)="+md.digest("abc"));
}

privateStringdigest(StringinputStr){
byte[]inputBytes=inputStr.getBytes();
intbyteLen=inputBytes.length;//长度(字节)
intgroupCount=0;//完整分组的个数
groupCount=byteLen/64;//每组512位(64字节)
long[]groups=null;//每个小组(64字节)再细分后的16个小组(4字节)

//处理每一个完整分组
for(intstep=0;step<groupCount;step++){
groups=divGroup(inputBytes,step*64);
trans(groups);//处理分组,核心算法
}

//处理完整分组后的尾巴
intrest=byteLen%64;//512位分组后的余数
byte[]tempBytes=newbyte[64];
if(rest<=56){
for(inti=0;i<rest;i++)
tempBytes[i]=inputBytes[byteLen-rest+i];
if(rest<56){
tempBytes[rest]=(byte)(1<<7);
for(inti=1;i<56-rest;i++)
tempBytes[rest+i]=0;
}
longlen=(long)(byteLen<<3);
for(inti=0;i<8;i++){
tempBytes[56+i]=(byte)(len&0xFFL);
len=len>>8;
}
groups=divGroup(tempBytes,0);
trans(groups);//处理分组
}else{
for(inti=0;i<rest;i++)
tempBytes[i]=inputBytes[byteLen-rest+i];
tempBytes[rest]=(byte)(1<<7);
for(inti=rest+1;i<64;i++)
tempBytes[i]=0;
groups=divGroup(tempBytes,0);
trans(groups);//处理分组

for(inti=0;i<56;i++)
tempBytes[i]=0;
longlen=(long)(byteLen<<3);
for(inti=0;i<8;i++){
tempBytes[56+i]=(byte)(len&0xFFL);
len=len>>8;
}
groups=divGroup(tempBytes,0);
trans(groups);//处理分组
}

//将Hash值转换成十六进制的字符串
StringresStr="";
longtemp=0;
for(inti=0;i<4;i++){
for(intj=0;j<4;j++){
temp=result[i]&0x0FL;
Stringa=hexs[(int)(temp)];
result[i]=result[i]>>4;
temp=result[i]&0x0FL;
resStr+=hexs[(int)(temp)]+a;
result[i]=result[i]>>4;
}
}
returnresStr;
}

/**
*从inputBytes的index开始取512位,作为新的分组
*将每一个512位的分组再细分成16个小组,每个小组64位(8个字节)
*@paraminputBytes
*@paramindex
*@return
*/
privatestaticlong[]divGroup(byte[]inputBytes,intindex){
long[]temp=newlong[16];
for(inti=0;i<16;i++){
temp[i]=b2iu(inputBytes[4*i+index])|
(b2iu(inputBytes[4*i+1+index]))<<8|
(b2iu(inputBytes[4*i+2+index]))<<16|
(b2iu(inputBytes[4*i+3+index]))<<24;
}
returntemp;
}

/**
*这时不存在符号位(符号位存储不再是代表正负),所以需要处理一下
*@paramb
*@return
*/
publicstaticlongb2iu(byteb){
returnb<0?b&0x7F+128:b;
}

/**
*主要的操作,四轮循环
*@paramgroups[]--每一个分组512位(64字节)
*/
privatevoidtrans(long[]groups){
longa=result[0],b=result[1],c=result[2],d=result[3];
/*第一轮*/
a=FF(a,b,c,d,groups[0],S11,0xd76aa478L);/*1*/
d=FF(d,a,b,c,groups[1],S12,0xe8c7b756L);/*2*/
c=FF(c,d,a,b,groups[2],S13,0x242070dbL);/*3*/
b=FF(b,c,d,a,groups[3],S14,0xc1bdceeeL);/*4*/
a=FF(a,b,c,d,groups[4],S11,0xf57c0fafL);/*5*/
d=FF(d,a,b,c,groups[5],S12,0x4787c62aL);/*6*/
c=FF(c,d,a,b,groups[6],S13,0xa8304613L);/*7*/
b=FF(b,c,d,a,groups[7],S14,0xfd469501L);/*8*/
a=FF(a,b,c,d,groups[8],S11,0x698098d8L);/*9*/
d=FF(d,a,b,c,groups[9],S12,0x8b44f7afL);/*10*/
c=FF(c,d,a,b,groups[10],S13,0xffff5bb1L);/*11*/
b=FF(b,c,d,a,groups[11],S14,0x895cd7beL);/*12*/
a=FF(a,b,c,d,groups[12],S11,0x6b901122L);/*13*/
d=FF(d,a,b,c,groups[13],S12,0xfd987193L);/*14*/
c=FF(c,d,a,b,groups[14],S13,0xa679438eL);/*15*/
b=FF(b,c,d,a,groups[15],S14,0x49b40821L);/*16*/

/*第二轮*/
a=GG(a,b,c,d,groups[1],S21,0xf61e2562L);/*17*/
d=GG(d,a,b,c,groups[6],S22,0xc040b340L);/*18*/
c=GG(c,d,a,b,groups[11],S23,0x265e5a51L);/*19*/
b=GG(b,c,d,a,groups[0],S24,0xe9b6c7aaL);/*20*/
a=GG(a,b,c,d,groups[5],S21,0xd62f105dL);/*21*/
d=GG(d,a,b,c,groups[10],S22,0x2441453L);/*22*/
c=GG(c,d,a,b,groups[15],S23,0xd8a1e681L);/*23*/
b=GG(b,c,d,a,groups[4],S24,0xe7d3fbc8L);/*24*/
a=GG(a,b,c,d,groups[9],S21,0x21e1cde6L);/*25*/
d=GG(d,a,b,c,groups[14],S22,0xc33707d6L);/*26*/
c=GG(c,d,a,b,groups[3],S23,0xf4d50d87L);/*27*/
b=GG(b,c,d,a,groups[8],S24,0x455a14edL);/*28*/
a=GG(a,b,c,d,groups[13],S21,0xa9e3e905L);/*29*/
d=GG(d,a,b,c,groups[2],S22,0xfcefa3f8L);/*30*/
c=GG(c,d,a,b,groups[7],S23,0x676f02d9L);/*31*/
b=GG(b,c,d,a,groups[12],S24,0x8d2a4c8aL);/*32*/

/*第三轮*/
a=HH(a,b,c,d,groups[5],S31,0xfffa3942L);/*33*/
d=HH(d,a,b,c,groups[8],S32,0x8771f681L);/*34*/
c=HH(c,d,a,b,groups[11],S33,0x6d9d6122L);/*35*/
b=HH(b,c,d,a,groups[14],S34,0xfde5380cL);/*36*/
a=HH(a,b,c,d,groups[1],S31,0xa4beea44L);/*37*/
d=HH(d,a,b,c,groups[4],S32,0x4bdecfa9L);/*38*/
c=HH(c,d,a,b,groups[7],S33,0xf6bb4b60L);/*39*/
b=HH(b,c,d,a,groups[10],S34,0xbebfbc70L);/*40*/
a=HH(a,b,c,d,groups[13],S31,0x289b7ec6L);/*41*/
d=HH(d,a,b,c,groups[0],S32,0xeaa127faL);/*42*/
c=HH(c,d,a,b,groups[3],S33,0xd4ef3085L);/*43*/
b=HH(b,c,d,a,groups[6],S34,0x4881d05L);/*44*/
a=HH(a,b,c,d,groups[9],S31,0xd9d4d039L);/*45*/
d=HH(d,a,b,c,groups[12],S32,0xe6db99e5L);/*46*/
c=HH(c,d,a,b,groups[15],S33,0x1fa27cf8L);/*47*/
b=HH(b,c,d,a,groups[2],S34,0xc4ac5665L);/*48*/

/*第四轮*/
a=II(a,b,c,d,groups[0],S41,0xf4292244L);/*49*/
d=II(d,a,b,c,groups[7],S42,0x432aff97L);/*50*/
c=II(c,d,a,b,groups[14],S43,0xab9423a7L);/*51*/
b=II(b,c,d,a,groups[5],S44,0xfc93a039L);/*52*/
a=II(a,b,c,d,groups[12],S41,0x655b59c3L);/*53*/
d=II(d,a,b,c,groups[3],S42,0x8f0ccc92L);/*54*/
c=II(c,d,a,b,groups[10],S43,0xffeff47dL);/*55*/
b=II(b,c,d,a,groups[1],S44,0x85845dd1L);/*56*/
a=II(a,b,c,d,groups[8],S41,0x6fa87e4fL);/*57*/
d=II(d,a,b,c,groups[15],S42,0xfe2ce6e0L);/*58*/
c=II(c,d,a,b,groups[6],S43,0xa3014314L);/*59*/
b=II(b,c,d,a,groups[13],S44,0x4e0811a1L);/*60*/
a=II(a,b,c,d,groups[4],S41,0xf7537e82L);/*61*/
d=II(d,a,b,c,groups[11],S42,0xbd3af235L);/*62*/
c=II(c,d,a,b,groups[2],S43,0x2ad7d2bbL);/*63*/
b=II(b,c,d,a,groups[9],S44,0xeb86d391L);/*64*/

/*加入到之前计算的结果当中*/
result[0]+=a;
result[1]+=b;
result[2]+=c;
result[3]+=d;
result[0]=result[0]&0xFFFFFFFFL;
result[1]=result[1]&0xFFFFFFFFL;
result[2]=result[2]&0xFFFFFFFFL;
result[3]=result[3]&0xFFFFFFFFL;
}

/**
*下面是处理要用到的线性函数
*/
privatestaticlongF(longx,longy,longz){
return(x&y)|((~x)&z);
}

privatestaticlongG(longx,longy,longz){
return(x&z)|(y&(~z));
}

privatestaticlongH(longx,longy,longz){
returnx^y^z;
}

privatestaticlongI(longx,longy,longz){
returny^(x|(~z));
}

privatestaticlongFF(longa,longb,longc,longd,longx,longs,
longac){
a+=(F(b,c,d)&0xFFFFFFFFL)+x+ac;
a=((a&0xFFFFFFFFL)<<s)|((a&0xFFFFFFFFL)>>>(32-s));
a+=b;
return(a&0xFFFFFFFFL);
}

privatestaticlongGG(longa,longb,longc,longd,longx,longs,
longac){
a+=(G(b,c,d)&0xFFFFFFFFL)+x+ac;
a=((a&0xFFFFFFFFL)<<s)|((a&0xFFFFFFFFL)>>>(32-s));
a+=b;
return(a&0xFFFFFFFFL);
}

privatestaticlongHH(longa,longb,longc,longd,longx,longs,
longac){
a+=(H(b,c,d)&0xFFFFFFFFL)+x+ac;
a=((a&0xFFFFFFFFL)<<s)|((a&0xFFFFFFFFL)>>>(32-s));
a+=b;
return(a&0xFFFFFFFFL);
}

privatestaticlongII(longa,longb,longc,longd,longx,longs,
longac){
a+=(I(b,c,d)&0xFFFFFFFFL)+x+ac;
a=((a&0xFFFFFFFFL)<<s)|((a&0xFFFFFFFFL)>>>(32-s));
a+=b;
return(a&0xFFFFFFFFL);
}
}
MD5安全性:
普遍认为MD5是很安全,因为暴力破解的时间是一般人无法接受的。实际上如果把用户的密码MD5处理后再存储到数据库,其实是很不安全的。因为用户的密码是比较短的,而且很多用户的密码都使用生日,手机号码,身份证号码,电话号码等等。或者使用常用的一些吉利的数字,或者某个英文单词。如果我把常用的密码先MD5处理,把数据存储起来,然后再跟你的MD5结果匹配,这时我就有可能得到明文。比如某个MD5破解网站 http://www.cmd5.com/default.aspx ,我把其网站下的公告复制如下
我觉得只需要将上面我写的MD5的标准幻数A,B,C,D的值修改一下,修改后也不是MD5算法了,因为不能保证唯一性。这样就算别人得到32位的值,他如果不知道幻数的值是无法还原明文的。就算得到了幻数,也是很难破解的。
JAVA实现MD5
在java中实现MD5是很简单的,在包java.security有个类MessageDigest。官方文档如下
packagewoxingwosu;
/************************************************
*MD5算法
*@author我行我素
*@Date2007-07-06
*************************************************/
importjava.security.MessageDigest;

public
class
MyMD5
{

staticchar[]hex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
publicstaticvoidmain(String[]args){
try{
MessageDigestmd5=MessageDigest.getInstance("MD5");//申明使用MD5算法
md5.update("a".getBytes());//
System.out.println("md5(a)="+byte2str(md5.digest()));
md5.update("a".getBytes());
md5.update("bc".getBytes());
System.out.println("md5(abc)="+byte2str(md5.digest()));
}catch(Exceptione){
e.printStackTrace();
}
}

/**
*将字节数组转换成十六进制字符串
*@parambytes
*@return
*/
privatestaticStringbyte2str(byte[]bytes){
intlen=bytes.length;
StringBufferresult=newStringBuffer();
for(inti=0;i<len;i++){
bytebyte0=bytes[i];
result.append(hex[byte0>>>4&0xf]);
result.append(hex[byte0&0xf]);
}
returnresult.toString();
}
}
另外附上其他版本的MD5算法的实现(
来自网络
)
1) JS版的MD5 (调用方法:md5(明文))
/*
*AJavaScriptimplementationoftheRSADataSecurity,Inc.MD5Message
*DigestAlgorithm,asdefinedinRFC1321.
*Version2.1Copyright(C)PaulJohnston1999-2002.
*Othercontributors:GregHolt,AndrewKepert,Ydnar,Lostinet
*DistributedundertheBSDLicense
*Seehttp://pajhome.org.uk/crypt/md5formoreinfo.
*/

/*
*Configurablevariables.Youmayneedtotweakthesetobecompatiblewith
*theserver-side,butthedefaultsworkinmostcases.
*/
var
hexcase
=
1
;
/*hexoutputformat.0-lowercase;1-uppercase*/
var
b64pad
=
""
;
/*base-64padcharacter."="forstrictRFCcompliance*/
var
chrsz
=
8
;
/*bitsperinputcharacter.8-ASCII;16-Unicode*/

/*
*Thesearethefunctionsyou'llusuallywanttocall
*Theytakestringargumentsandreturneitherhexorbase-64encodedstrings
*/
function
hex_md5(s)
{returnbinl2hex(core_md5(str2binl(s),s.length*chrsz));}
function
str_md5(s)
{returnbinl2str(core_md5(str2binl(s),s.length*chrsz));}
function
hex_hmac_md5(key,data)
{returnbinl2hex(core_hmac_md5(key,data));}
function
str_hmac_md5(key,data)
{returnbinl2str(core_hmac_md5(key,data));}

/*
*CalculatetheMD5ofanarrayoflittle-endianwords,andabitlength
*/
function
core_md5(x,len)
{
/*appendpadding*/
x[len>>5]|=0x80<<((len)%32);
x[(((len+64)>>>9)<<4)+14]=len;

vara=1732584193;
varb=-271733879;
varc=-1732584194;
vard=271733878;

for(vari=0;i<x.length;i+=16)
{
varolda=a;
varoldb=b;
varoldc=c;
varoldd=d;

a=md5_ff(a,b,c,d,x[i+0],7,-680876936);
d=md5_ff(d,a,b,c,x[i+1],12,-389564586);
c=md5_ff(c,d,a,b,x[i+2],17,606105819);
b=md5_ff(b,c,d,a,x[i+3],22,-1044525330);
a=md5_ff(a,b,c,d,x[i+4],7,-176418897);
d=md5_ff(d,a,b,c,x[i+5],12,1200080426);
c=md5_ff(c,d,a,b,x[i+6],17,-1473231341);
b=md5_ff(b,c,d,a,x[i+7],22,-45705983);
a=md5_ff(a,b,c,d,x[i+8],7,1770035416);
d=md5_ff(d,a,b,c,x[i+9],12,-1958414417);
c=md5_ff(c,d,a,b,x[i+10],17,-42063);
b=md5_ff(b,c,d,a,x[i+11],22,-1990404162);
a=md5_ff(a,b,c,d,x[i+12],7,1804603682);
d=md5_ff(d,a,b,c,x[i+13],12,-40341101);
c=md5_ff(c,d,a,b,x[i+14],17,-1502002290);
b=md5_ff(b,c,d,a,x[i+15],22,1236535329);

a=md5_gg(a,b,c,d,x[i+1],5,-165796510);
d=md5_gg(d,a,b,c,x[i+6],9,-1069501632);
c=md5_gg(c,d,a,b,x[i+11],14,643717713);
b=md5_gg(b,c,d,a,x[i+0],20,-373897302);
a=md5_gg(a,b,c,d,x[i+5],5,-701558691);
d=md5_gg(d,a,b,c,x[i+10],9,38016083);
c=md5_gg(c,d,a,b,x[i+15],14,-660478335);
b=md5_gg(b,c,d,a,x[i+4],20,-405537848);
a=md5_gg(a,b,c,d,x[i+9],5,568446438);
d=md5_gg(d,a,b,c,x[i+14],9,-1019803690);
c=md5_gg(c,d,a,b,x[i+3],14,-187363961);
b=md5_gg(b,c,d,a,x[i+8],20,1163531501);
a=md5_gg(a,b,c,d,x[i+13],5,-1444681467);
d=md5_gg(d,a,b,c,x[i+2],9,-51403784);
c=md5_gg(c,d,a,b,x[i+7],14,1735328473);
b=md5_gg(b,c,d,a,x[i+12],20,-1926607734);

a=md5_hh(a,b,c,d,x[i+5],4,-378558);
d=md5_hh(d,a,b,c,x[i+8],11,-2022574463);
c=md5_hh(c,d,a,b,x[i+11],16,1839030562);
b=md5_hh(b,c,d,a,x[i+14],23,-35309556);
a=md5_hh(a,b,c,d,x[i+1],4,-1530992060);
d=md5_hh(d,a,b,c,x[i+4],11,1272893353);
c=md5_hh(c,d,a,b,x[i+7],16,-155497632);
b=md5_hh(b,c,d,a,x[i+10],23,-1094730640);
a=md5_hh(a,b,c,d,x[i+13],4,681279174);
d=md5_hh(d,a,b,c,x[i+0],11,-358537222);
c=md5_hh(c,d,a,b,x[i+3],16,-722521979);
b=md5_hh(b,c,d,a,x[i+6],23,76029189);
a=md5_hh(a,b,c,d,x[i+9],4,-640364487);
d=md5_hh(d,a,b,c,x[i+12],11,-421815835);
c=md5_hh(c,d,a,b,x[i+15],16,530742520);
b=md5_hh(b,c,d,a,x[i+2],23,-995338651);

a=md5_ii(a,b,c,d,x[i+0],6,-198630844);
d=md5_ii(d,a,b,c,x[i+7],10,1126891415);
c=md5_ii(c,d,a,b,x[i+14],15,-1416354905);
b=md5_ii(b,c,d,a,x[i+5],21,-57434055);
a=md5_ii(a,b,c,d,x[i+12],6,1700485571);
d=md5_ii(d,a,b,c,x[i+3],10,-1894986606);
c=md5_ii(c,d,a,b,x[i+10],15,-1051523);
b=md5_ii(b,c,d,a,x[i+1],21,-2054922799);
a=md5_ii(a,b,c,d,x[i+8],6,1873313359);
d=md5_ii(d,a,b,c,x[i+15],10,-30611744);
c=md5_ii(c,d,a,b,x[i+6],15,-1560198380);
b=md5_ii(b,c,d,a,x[i+13],21,1309151649);
a=md5_ii(a,b,c,d,x[i+4],6,-145523070);
d=md5_ii(d,a,b,c,x[i+11],10,-1120210379);
c=md5_ii(c,d,a,b,x[i+2],15,718787259);
b=md5_ii(b,c,d,a,x[i+9],21,-343485551);

a=safe_add(a,olda);
b=safe_add(b,oldb);
c=safe_add(c,oldc);
d=safe_add(d,oldd);
}
returnArray(a,b,c,d);

}

/*
*Thesefunctionsimplementthefourbasicoperationsthealgorithmuses.
*/
function
md5_cmn(q,a,b,x,s,t)
{
returnsafe_add(bit_rol(safe_add(safe_add(a,q),safe_add(x,t)),s),b);
}
function
md5_ff(a,b,c,d,x,s,t)
{
returnmd5_cmn((b&c)|((~b)&d),a,b,x,s,t);
}
function
md5_gg(a,b,c,d,x,s,t)
{
returnmd5_cmn((b&d)|(c&(~d)),a,b,x,s,t);
}
function
md5_hh(a,b,c,d,x,s,t)
{
returnmd5_cmn(b^c^d,a,b,x,s,t);
}
function
md5_ii(a,b,c,d,x,s,t)
{
returnmd5_cmn(c^(b|(~d)),a,b,x,s,t);
}

/*
*CalculatetheHMAC-MD5,ofakeyandsomedata
*/
function
core_hmac_md5(key,data)
{
varbkey=str2binl(key);
if(bkey.length>16)bkey=core_md5(bkey,key.length*chrsz);

varipad=Array(16),opad=Array(16);
for(vari=0;i<16;i++)
{
ipad[i]=bkey[i]^0x36363636;
opad[i]=bkey[i]^0x5C5C5C5C;
}

varhash=core_md5(ipad.concat(str2binl(data)),512+data.length*chrsz);
returncore_md5(opad.concat(hash),512+128);
}

/*
*Addintegers,wrappingat2^32.Thisuses16-bitoperationsinternally
*toworkaroundbugsinsomeJSinterpreters.
*/
function
safe_add(x,y)
{
varlsw=(x&0xFFFF)+(y&0xFFFF);
varmsw=(x>>16)+(y>>16)+(lsw>>16);
return(msw<<16)|(lsw&0xFFFF);
}

/*
*Bitwiserotatea32-bitnumbertotheleft.
*/
function
bit_rol(num,cnt)
{
return(num<<cnt)|(num>>>(32-cnt));
}

/*
*Convertastringtoanarrayoflittle-endianwords
*IfchrszisASCII,characters>255havetheirhi-bytesilentlyignored.
*/
function
str2binl(str)
{
varbin=Array();
varmask=(1<<chrsz)-1;
for(vari=0;i<str.length*chrsz;i+=chrsz)
bin[i>>5]|=(str.charCodeAt(i/chrsz)&mask)<<(i%32);
returnbin;
}

/*
*Convertanarrayoflittle-endianwordstoastring
*/
function
binl2str(bin)
{
varstr="";
varmask=(1<<chrsz)-1;
for(vari=0;i<bin.length*32;i+=chrsz)
str+=String.fromCharCode((bin[i>>5]>>>(i%32))&mask);
returnstr;
}

/*
*Convertanarrayoflittle-endianwordstoahexstring.
*/
function
binl2hex(binarray)
{
varhex_tab=hexcase?"0123456789ABCDEF":"0123456789abcdef";
varstr="";
for(vari=0;i<binarray.length*4;i++)
{
str+=hex_tab.charAt((binarray[i>>2]>>((i%4)*8+4))&0xF)+
hex_tab.charAt((binarray[i>>2]>>((i%4)*8))&0xF);
}
returnstr;
}

String.prototype.trim
=
function
()
{returnthis.replace(/(^s*)|(s*$)/g,"");}

function
md5(text)
{
returnhex_md5(text.trim());
}
MD5功能:
输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
不同的输入得到的不同的结果(唯一性);
根据128位的输出结果不可能反推出输入的信息(不可逆);
MD5属不属于加密算法:
认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有解密算法,所以这部分人认为MD5只能属于算法,不能称为加密算法;
认为属于的人是因为他们觉得经过MD5处理后看不到原文,即已经将原文加密,所以认为MD5属于加密算法;我个人支持后者。
MD5用途:
1、 防止被篡改 :
1)比如发送一个电子文档,发送前,我先得到MD5的输出结果a。然后在对方收到电子文档后,对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。2)比如我提供文件下载,为了防止不法分子在安装程序中添加木马,我可以在网站上公布由安装文件得到的MD5输出结果。3)SVN在检测文件是否在CheckOut后被修改过,也是用到了MD5.
2、 防止直接看到明文 :
现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值,也无法知道用户的密码(其实这样是不安全的,后面我会提到)。(比如在UNIX系统中用户的密码就是以MD5(或其它类似的算法)经加密后存储在文件系统中。当用户登录的时候,系统把用户输入的密码计算成MD5值,然后再去和保存在文件系统中的MD5值进行比较,进而确定输入的密码是否正确。通过这样的步骤,系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道,而且还在一定程度上增加了密码被破解的难度。)
3、 防止抵赖(数字签名):
这需要一个第三方认证机构。例如A写了一个文件,认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的,权威机构只需对此文件重新产生摘要信息,然后跟记录在册的摘要信息进行比对,相同的话,就证明是A写的了。这就是所谓的“数字签名”。
MD5算法过程:
对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
第一步、填充:如果输入信息的长度(bit)对512求余的结果不等于448,就需要填充使得对512求余的结果等于448。填充的方法是填充一个1和n个0。填充完后,信息的长度就为N*512+448(bit);
第二步、记录信息长度:用64位来存储填充前信息长度。这64位加在第一步结果的后面,这样信息长度就变为N*512+448+64=(N+1)*512位。
第三步、装入标准的幻数(四个整数):标准的幻数(物理顺序)是(A=(01234567) 16 ,B=(89ABCDEF) 16 ,C=(FEDCBA98) 16 ,D=(76543210) 16 )。如果在程序中定义应该是(A=0X67452301L,B=0XEFCDAB89L,C=0X98BADCFEL,D=0X10325476L)。有点晕哈,其实想一想就明白了。
第四步、四轮循环运算:循环的次数是分组的个数(N+1)
1)将每一512字节细分成16个小组,每个小组64位(8个字节)
2)先认识四个线性函数(&是与,|是或,~是非,^是异或)
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))
G(X,Y,Z) = (X & Z) | (Y & ( ~ Z))
H(X,Y,Z) = X ^ Y ^ Z
I(X,Y,Z) = Y ^ (X | ( ~ Z))
3)设Mj表示消息的第j个子分组(从0到15),<<<s表示循环左移s位,则四种操作为:
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)
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)
4)四轮运算
第一轮
a = FF(a,b,c,d,M0, 7 , 0xd76aa478 )
b = FF(d,a,b,c,M1, 12 , 0xe8c7b756 )
c = FF(c,d,a,b,M2, 17 , 0x242070db )
d = FF(b,c,d,a,M3, 22 , 0xc1bdceee )
a = FF(a,b,c,d,M4, 7 , 0xf57c0faf )
b = FF(d,a,b,c,M5, 12 , 0x4787c62a )
c = FF(c,d,a,b,M6, 17 , 0xa8304613 )
d = FF(b,c,d,a,M7, 22 , 0xfd469501 )
a = FF(a,b,c,d,M8, 7 , 0x698098d8 )
b = FF(d,a,b,c,M9, 12 , 0x8b44f7af )
c = FF(c,d,a,b,M10, 17 , 0xffff5bb1 )
d = FF(b,c,d,a,M11, 22 , 0x895cd7be )
a = FF(a,b,c,d,M12, 7 , 0x6b901122 )
b = FF(d,a,b,c,M13, 12 , 0xfd987193 )
c = FF(c,d,a,b,M14, 17 , 0xa679438e )
d = FF(b,c,d,a,M15, 22 , 0x49b40821 )
第二轮
a = GG(a,b,c,d,M1, 5 , 0xf61e2562 )
b = GG(d,a,b,c,M6, 9 , 0xc040b340 )
c = GG(c,d,a,b,M11, 14 , 0x265e5a51 )
d = GG(b,c,d,a,M0, 20 , 0xe9b6c7aa )
a = GG(a,b,c,d,M5, 5 , 0xd62f105d )
b = GG(d,a,b,c,M10, 9 , 0x02441453 )
c = GG(c,d,a,b,M15, 14 , 0xd8a1e681 )
d = GG(b,c,d,a,M4, 20 , 0xe7d3fbc8 )
a = GG(a,b,c,d,M9, 5 , 0x21e1cde6 )
b = GG(d,a,b,c,M14, 9 , 0xc33707d6 )
c = GG(c,d,a,b,M3, 14 , 0xf4d50d87 )
d = GG(b,c,d,a,M8, 20 , 0x455a14ed )
a = GG(a,b,c,d,M13, 5 , 0xa9e3e905 )
b = GG(d,a,b,c,M2, 9 , 0xfcefa3f8 )
c = GG(c,d,a,b,M7, 14 , 0x676f02d9 )
d = GG(b,c,d,a,M12, 20 , 0x8d2a4c8a )
第三轮
a = HH(a,b,c,d,M5, 4 , 0xfffa3942 )
b = HH(d,a,b,c,M8, 11 , 0x8771f681 )
c = HH(c,d,a,b,M11, 16 , 0x6d9d6122 )
d = HH(b,c,d,a,M14, 23 , 0xfde5380c )
a = HH(a,b,c,d,M1, 4 , 0xa4beea44 )
b = HH(d,a,b,c,M4, 11 , 0x4bdecfa9 )
c = HH(c,d,a,b,M7, 16 , 0xf6bb4b60 )
d = HH(b,c,d,a,M10, 23 , 0xbebfbc70 )
a = HH(a,b,c,d,M13, 4 , 0x289b7ec6 )
b = HH(d,a,b,c,M0, 11 , 0xeaa127fa )
c = HH(c,d,a,b,M3, 16 , 0xd4ef3085 )
d = HH(b,c,d,a,M6, 23 , 0x04881d05 )
a = HH(a,b,c,d,M9, 4 , 0xd9d4d039 )
b = HH(d,a,b,c,M12, 11 , 0xe6db99e5 )
c = HH(c,d,a,b,M15, 16 , 0x1fa27cf8 )
d = HH(b,c,d,a,M2, 23 , 0xc4ac5665 )
第四轮
a = II(a,b,c,d,M0, 6 , 0xf4292244 )
b = II(d,a,b,c,M7, 10 , 0x432aff97 )
c = II(c,d,a,b,M14, 15 , 0xab9423a7 )
d = II(b,c,d,a,M5, 21 , 0xfc93a039 )
a = II(a,b,c,d,M12, 6 , 0x655b59c3 )
b = II(d,a,b,c,M3, 10 , 0x8f0ccc92 )
c = II(c,d,a,b,M10, 15 , 0xffeff47d )
d = II(b,c,d,a,M1, 21 , 0x85845dd1 )
a = II(a,b,c,d,M8, 6 , 0x6fa87e4f )
b = II(d,a,b,c,M15, 10 , 0xfe2ce6e0 )
c = II(c,d,a,b,M6, 15 , 0xa3014314 )
d = II(b,c,d,a,M13, 21 , 0x4e0811a1 )
a = II(a,b,c,d,M4, 6 , 0xf7537e82 )
b = II(d,a,b,c,M11, 10 , 0xbd3af235 )
c = II(c,d,a,b,M2, 15 , 0x2ad7d2bb )
d = II(b,c,d,a,M9, 21 , 0xeb86d391 )
a = FF(a,b,c,d,M0, 7 , 0xd76aa478 )
b = FF(d,a,b,c,M1, 12 , 0xe8c7b756 )
c = FF(c,d,a,b,M2, 17 , 0x242070db )
d = FF(b,c,d,a,M3, 22 , 0xc1bdceee )
a = FF(a,b,c,d,M4, 7 , 0xf57c0faf )
b = FF(d,a,b,c,M5, 12 , 0x4787c62a )
c = FF(c,d,a,b,M6, 17 , 0xa8304613 )
d = FF(b,c,d,a,M7, 22 , 0xfd469501 )
a = FF(a,b,c,d,M8, 7 , 0x698098d8 )
b = FF(d,a,b,c,M9, 12 , 0x8b44f7af )
c = FF(c,d,a,b,M10, 17 , 0xffff5bb1 )
d = FF(b,c,d,a,M11, 22 , 0x895cd7be )
a = FF(a,b,c,d,M12, 7 , 0x6b901122 )
b = FF(d,a,b,c,M13, 12 , 0xfd987193 )
c = FF(c,d,a,b,M14, 17 , 0xa679438e )
d = FF(b,c,d,a,M15, 22 , 0x49b40821 )
第二轮
a = GG(a,b,c,d,M1, 5 , 0xf61e2562 )
b = GG(d,a,b,c,M6, 9 , 0xc040b340 )
c = GG(c,d,a,b,M11, 14 , 0x265e5a51 )
d = GG(b,c,d,a,M0, 20 , 0xe9b6c7aa )
a = GG(a,b,c,d,M5, 5 , 0xd62f105d )
b = GG(d,a,b,c,M10, 9 , 0x02441453 )
c = GG(c,d,a,b,M15, 14 , 0xd8a1e681 )
d = GG(b,c,d,a,M4, 20 , 0xe7d3fbc8 )
a = GG(a,b,c,d,M9, 5 , 0x21e1cde6 )
b = GG(d,a,b,c,M14, 9 , 0xc33707d6 )
c = GG(c,d,a,b,M3, 14 , 0xf4d50d87 )
d = GG(b,c,d,a,M8, 20 , 0x455a14ed )
a = GG(a,b,c,d,M13, 5 , 0xa9e3e905 )
b = GG(d,a,b,c,M2, 9 , 0xfcefa3f8 )
c = GG(c,d,a,b,M7, 14 , 0x676f02d9 )
d = GG(b,c,d,a,M12, 20 , 0x8d2a4c8a )
第三轮
a = HH(a,b,c,d,M5, 4 , 0xfffa3942 )
b = HH(d,a,b,c,M8, 11 , 0x8771f681 )
c = HH(c,d,a,b,M11, 16 , 0x6d9d6122 )
d = HH(b,c,d,a,M14, 23 , 0xfde5380c )
a = HH(a,b,c,d,M1, 4 , 0xa4beea44 )
b = HH(d,a,b,c,M4, 11 , 0x4bdecfa9 )
c = HH(c,d,a,b,M7, 16 , 0xf6bb4b60 )
d = HH(b,c,d,a,M10, 23 , 0xbebfbc70 )
a = HH(a,b,c,d,M13, 4 , 0x289b7ec6 )
b = HH(d,a,b,c,M0, 11 , 0xeaa127fa )
c = HH(c,d,a,b,M3, 16 , 0xd4ef3085 )
d = HH(b,c,d,a,M6, 23 , 0x04881d05 )
a = HH(a,b,c,d,M9, 4 , 0xd9d4d039 )
b = HH(d,a,b,c,M12, 11 , 0xe6db99e5 )
c = HH(c,d,a,b,M15, 16 , 0x1fa27cf8 )
d = HH(b,c,d,a,M2, 23 , 0xc4ac5665 )
第四轮
a = II(a,b,c,d,M0, 6 , 0xf4292244 )
b = II(d,a,b,c,M7, 10 , 0x432aff97 )
c = II(c,d,a,b,M14, 15 , 0xab9423a7 )
d = II(b,c,d,a,M5, 21 , 0xfc93a039 )
a = II(a,b,c,d,M12, 6 , 0x655b59c3 )
b = II(d,a,b,c,M3, 10 , 0x8f0ccc92 )
c = II(c,d,a,b,M10, 15 , 0xffeff47d )
d = II(b,c,d,a,M1, 21 , 0x85845dd1 )
a = II(a,b,c,d,M8, 6 , 0x6fa87e4f )
b = II(d,a,b,c,M15, 10 , 0xfe2ce6e0 )
c = II(c,d,a,b,M6, 15 , 0xa3014314 )
d = II(b,c,d,a,M13, 21 , 0x4e0811a1 )
a = II(a,b,c,d,M4, 6 , 0xf7537e82 )
b = II(d,a,b,c,M11, 10 , 0xbd3af235 )
c = II(c,d,a,b,M2, 15 , 0x2ad7d2bb )
d = II(b,c,d,a,M9, 21 , 0xeb86d391 )
5)每轮循环后,将A,B,C,D分别加上a,b,c,d,然后进入下一循环。
如果上面的过程用JAVA代码来实现的话,代码如下:


























































































































































































































































































普遍认为MD5是很安全,因为暴力破解的时间是一般人无法接受的。实际上如果把用户的密码MD5处理后再存储到数据库,其实是很不安全的。因为用户的密码是比较短的,而且很多用户的密码都使用生日,手机号码,身份证号码,电话号码等等。或者使用常用的一些吉利的数字,或者某个英文单词。如果我把常用的密码先MD5处理,把数据存储起来,然后再跟你的MD5结果匹配,这时我就有可能得到明文。比如某个MD5破解网站 http://www.cmd5.com/default.aspx ,我把其网站下的公告复制如下
md5破解、动网论坛密码破解等不再需要用穷举法,本站共有md5记录235亿条,还在不断增长中,已包含10位及10位以下数字、7位字母、部分7位字母+数字,全部6位及以下字母加数字等组合,并针对国内用户做了大量优化,例如已经包含所有手机号码、全国部分大中城市固定电话号码、百家姓、常用拼音等大量组合,另加入了某大型网站真实会员密码数据10万条。本站数据量大,查询速度快,同时支持16位及32位密码查询。通过对10万会员的真实动网论坛样本数据的测试,本站对于动网论坛密码的命中率达到83
%
。
本站4T的硬盘已经上线,正在生成数据,预计需要2个月左右时间,到时候本站能查询到12位数字和9位字母。
你可以用你的生日,手机号码去测试一下。
本站4T的硬盘已经上线,正在生成数据,预计需要2个月左右时间,到时候本站能查询到12位数字和9位字母。
我觉得只需要将上面我写的MD5的标准幻数A,B,C,D的值修改一下,修改后也不是MD5算法了,因为不能保证唯一性。这样就算别人得到32位的值,他如果不知道幻数的值是无法还原明文的。就算得到了幻数,也是很难破解的。
JAVA实现MD5
在java中实现MD5是很简单的,在包java.security有个类MessageDigest。官方文档如下
MessageDigest类为应用程序提供信息摘要算法的功能,如MD5或SHA算法。信息摘要是安全的单向哈希函数,它接收任意大小的数据,输出固定长度的哈希值。
MessageDigest对象开始被初始化。该对象通过使用update方法处理数据。任何时候都可以调用reset方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用digest方法之一完成哈希计算。
对于给定数量的更新数据,digest方法只能被调用一次。digest被调用后,MessageDigest对象被重新设置成其初始状态。
使用MessageDigest很简单,例如
MessageDigest对象开始被初始化。该对象通过使用update方法处理数据。任何时候都可以调用reset方法重置摘要。一旦所有需要更新的数据都已经被更新了,应该调用digest方法之一完成哈希计算。
对于给定数量的更新数据,digest方法只能被调用一次。digest被调用后,MessageDigest对象被重新设置成其初始状态。









































1) JS版的MD5 (调用方法:md5(明文))






































































































































































































































