MD5加密算法解释——Java简单应用(java.security.MessageDigest)
Posted 烟雨蒋楠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MD5加密算法解释——Java简单应用(java.security.MessageDigest)相关的知识,希望对你有一定的参考价值。
MD5解释
MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由罗纳德·李维斯特设计,于1992年公开,用以替换MD4算法。MD5在MD4的基础上增加了"安全带"(safety-belts)的概念。虽然MD5比MD4复杂度大一些,但却更为安全。
知识补给:
1、MD4算法需要填补信息以确保信息的比特位长度减去448后能被512整除(信息比特位长度 mod 512 = 448)。然后,一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damgard/merkle迭代结构的区块,而且每个区块要通过三个不同步骤的处理。
2、密码散列函数(Cryptographic hash function),又译为加密散列函数,是散列函数的一种。它被认为是一种单向函数,也就是说极其难以由散列函数输出的结果,回推输入的数据是什么。这样的单向函数被称为“现代密码学的驮马”。
3、散列函数(或散列算法,又称哈希函数,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
4、字节(Byte)是计算机信息技术用于计量存储容量的一种计量单位,也表示一些计算机编程语言中的数据类型和语言字符 。一个字节存储8位无符号数,储存的数值范围为0~255(2^8 - 1)。
Java实现MD5加密
本例中使用了java.security.MessageDigest这个类包来实现加密,实现方法如下:
import java.security.MessageDigest;
public class MD5 {
public static String Md5(String plainText) {
try {
//获取MD5消息摘要对象
//JDK 6 以上支持以下6种消息摘要算法,不区分大小写
// md5,sha(sha-1),md2,sha-256,sha-384,sha-512
MessageDigest md = MessageDigest.getInstance("MD5");
//通过指定的字节更新摘要
md.update(plainText.getBytes("UTF-8"));
//获取散列值的字节数组(完成哈希计算)
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
//将整数转换成16进制无符号数
buf.append(Integer.toHexString(i));
}
//将无符号整数转成字符串
return buf.toString();
} catch (Exception e) {
//e.printStackTrace();
return "";
}
}
}
在此编造一个手机号码进行加密,得到的结果如图所示:
备注:
本人通过网上MD5在线破解对str1与str2的32位摘要进行破解,发现对于str1,可以很轻易的碰撞出结果,但是对于str2,结果就很难碰撞出。这也得出了,对于手机号的加密,可以通过在手机号前面增加随机字符来增强安全性。该方法在企业中也很常见。
在百度百科中,也指出了MD5的弱点:
1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。
知识补给:MD5破解是通过摘要获取原文吗?
答案是否定的,这里所谓的破解,并非把摘要还原成原文。因为固定128位的摘要是有穷的,而原文数量是无穷的,每一个摘要都可以由若干个原文通过Hash得到。对于MD5的破解,实际上都属于【碰撞】。比如原文A通过MD5可以生成摘要M,我们并不需要把M还原成A,只需要找到原文B,生成同样的摘要M即可。即A和B通过MD5加密后的结果殊途同归。
例如:我们用18034587499作为登录密码,后台服务记录的密码为4a05e2a9cbcaa8aea34a0dff645adc55。此时,通过MD5碰撞出一个明文:6666(举例)。我们在登录时就不需要使用18034587499作为登录密码,直接使用6666登录即可骗过应用系统的验签,到达破解密码的效果。
MD5加密原理
MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联后将生成一个128位散列值。
1、首先需要对信息进行填充,使其字节长度对512求余数的结果等于448。因此,信息的字节长度(Bits Length)将被扩展至N*512+448,即N*64+56个字节(Bytes),N为一个正整数。
2、填充的方法,在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。然后再在这个结果后面附加一个以64位二进制表示的填充前的信息长度。经过这两步的处理,现在的信息字节长度=N*512+448+64=(N+1)*512,即长度恰好是512的整数倍数。这样做的原因是为满足后面处理中对信息长度的要求。
3、四轮循环运算,MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。当设置好这四个链接变量后,就开始进入算法的四轮循环运算,循环的次数是信息中512位信息分组的数目。
主循环逻辑:
1、每次操作对a,b,c和d中的其中三个作一次非线性函数运算:F=(b&c)|((~b)&d)
2、将所得结果加上 第四个变量、一个常数和文本的一个子分组:a+F+K[i]+M[g]
3、再将所得结果向右环移一个不定的数:shift(a+F+K[i]+M[g],s[i])
4、并加上a,b,c或d中之一:b = b+shift(a+F+K[i]+M[g],s[i])
5、最后用该结果取代a,b,c或d中之一:Btemp=b+Btemp
下面将加密原理的代码给出,供学有余力的读者学习。
public class MD5_test {
/*
* 四个链接变量
* 为固定值,经过研究所得
*/
private final int A=0x01234567;
private final int B=0x89abcdef;
private final int C=0xfedcba98;
private final int D=0x76543210;
/*
*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));//右移的时候,高位一定要补零,而不是补充符号位
}
/*
*主循环
* 1、每次操作对a,b,c和d中的其中三个作一次非线性函数运算:F=(b&c)|((~b)&d)
* 2、将所得结果加上 第四个变量、一个常数和文本的一个子分组:a+F+K[i]+M[g]
* 3、再将所得结果向右环移一个不定的数:shift(a+F+K[i]+M[g],s[i])
* 4、并加上a,b,c或d中之一:b = b+shift(a+F+K[i]+M[g],s[i])
* 5、最后用该结果取代a,b,c或d中之一:Btemp=b+Btemp
*/
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_test instance;
public static MD5_test getInstance(){
if(instance==null){
instance=new MD5_test();
}
return instance;
}
private MD5_test(){};
public static void main(String[] args){
String str=MD5_test.getInstance().getMD5("");
System.out.println(str);
}
}
有问题可以一起探讨,希望对你有帮助;
我的一小步,填坑一大步!!!
以上是关于MD5加密算法解释——Java简单应用(java.security.MessageDigest)的主要内容,如果未能解决你的问题,请参考以下文章