MD5简介:
MD5是典型的消息摘要算法,是广泛使用的消息摘要算法之一,更是消息摘要算法首要代表。其前身包括MD2、MD4。MD算法家族针对源数据会产生一个128位的消息摘要。在一般应用场景下,会将128二进制摘要信息转换为十六进制,可以得到一个32位的十六进制字符串。
MD5的发展经历了MD2、MD4系列算法:
· MD2:1989年,RSA的发明人之一(罗纳德·李维斯特)开发了MD2算法。
1)首先对信息进行数据补位,使信息的字节长度是16的倍数。
2)以一个16位的检验和作为补充信息追加到原信息的末尾。
3)最后根据这个新产生的信息计算出一个128位的散列值。
· MD4:1990年,罗纳德·李维斯特在MD2的基础上开发出了安全性更高的MD4算法。MD4仍需对信息进行数据补位。不同的是,这种补位使其信息的字节长度加上448个字节后成为512的倍数(信息字节长度mod 512 =448)。此外,关于MD4算的处理和MD2算法有很大的差别。但最终仍旧会获得一个128为的散列值。MD4算法对后续消息摘要算法起到了推动作用, 许多比较有名的消息摘要算法都是在MD4算法的基础上发展而来的,如MD5、SHA-1、RIPE-MD和HAVAL算法等。
· MD5:1991年,继MD4算法后,罗纳德·李维斯特开发了MD5算法,将MD算法推向成熟。MD5算法经MD2、MD4算法发展而来,算法复杂程度和安全度得到了大大的提高。但无论是哪种MD算法,最终都会得到一个128位的散列值。MD5执行效率略次于MD4,但在安全性方面,MD5更胜一筹。
MD5特性:
1)压缩性:任意长度数据,都可以计算出固定长度的MD5值。
2)容易计算:从原数据可以容易计算出MD5值。
3)抗修改性:对原数据进行任意改动,计算出的MD5值都会发生变化。
4)弱抗碰撞:已知原数据和MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
5)强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
应用场景:
· 软件校验
很多软件,尤其是安全性要求较高的软件,会在官网上公布软件的MD5值,用户下载软件后,可以自行计算软件MD5值,然后与官网公布的MD5值进行比较,确认软件是否被篡改过。
· 基于口令的加密
MD5也被用于基于口令的加密(Password Based Encryption,PBE),PBE的原理是将口令和盐(salt)混合后计算其MD5值,然后将这个散列值用作加密的秘钥。PBE可以防御针对口令的字典攻击。
· 消息认证码
消息认证码是将“发送者和接收者之间的共享秘钥”和“消息”进行混合后计算出的MD5。使用消息认证码可以检测并防止通信过程中的错误、篡改以及伪装。
· 数字签名
数字签名是现实社会中的签名和盖章这样的行为在数字世界中的实现。数字签名的处理过程非常耗时,因此一般不会对整个消息内容施加数字签名,而是先取消息内容的MD5值,对MD5值进行数字签名。
演示示例:
除了JDK外,很多开源工具也对MD算法做了实现,其中包括Bouncy Castle、Commons Codec等。
JDK原生示例:
package com.securitit.serialize.md5;
import java.security.MessageDigest;
import org.apache.commons.codec.binary.Hex;
public class JdkMdTester {
public static void main(String[] args) throws Exception {
String plainText = "Hello MD! This is my MD test program!";
byte[] md2Bts = encodeMd2(plainText.getBytes("UTF-8"));
System.out.println("MD2散列值:" + new String(Hex.encodeHex(md2Bts)));
byte[] md5Bts = encodeMd5(plainText.getBytes("UTF-8"));
System.out.println("MD5散列值:" + new String(Hex.encodeHex(md5Bts)));
}
public static byte[] encodeMd2(byte[] plainBts) throws Exception {
// 初始化MessageDigest.
MessageDigest md2 = MessageDigest.getInstance("MD4");
// 获取消息摘要.
return md2.digest(plainBts);
}
public static byte[] encodeMd5(byte[] plainBts) throws Exception {
// 初始化MessageDigest.
MessageDigest md5 = MessageDigest.getInstance("MD5");
// 获取消息摘要.
return md5.digest(plainBts);
}
}
输出结果:
MD2散列值:bdf364fb82e5a89292949610730142a0
MD5散列值:a505a9d12b9aa595095bd800f63a528f
Bouncy Castle示例:
第三方加密组件包Bouncy Castle是对JDK的补充,弥补了JDK未提供MD4算法的空白。
package com.securitit.serialize.md5;
import java.security.MessageDigest;
import java.security.Security;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class BouncyCastleMdTester {
public static void main(String[] args) throws Exception {
String plainText = "Hello MD! This is my MD test program!";
byte[] md4Bts = encodeMd4(plainText.getBytes("UTF-8"));
System.out.println("MD4散列值:" + new String(Hex.encodeHex(md4Bts)));
}
public static byte[] encodeMd4(byte[] plainBts) throws Exception {
// 加入BouncyCastleProvider支持.
Security.addProvider(new BouncyCastleProvider());
// 初始化MessageDigest.
MessageDigest md4 = MessageDigest.getInstance("MD4");
// 获取消息摘要.
return md4.digest(plainBts);
}
}
输出结果:
MD4散列值:fac1db98137baa42b32ee77614ba7884
Commons Codec示例:
Apache Commons Codec提供了消息摘要工具类DigestUtils,DigestUtils类对JDK提供的MessageDigest进行了封装,使得API的使用更加简洁。
package com.securitit.serialize.md5;
import org.apache.commons.codec.digest.DigestUtils;
public class CommonsCodecMdTester {
public static void main(String[] args) throws Exception {
String plainText = "Hello MD! This is my MD test program!";
System.out.println("MD2散列值:" + DigestUtils.md2Hex(plainText.getBytes("UTF-8")));
System.out.println("MD5散列值:" + DigestUtils.md5Hex(plainText.getBytes("UTF-8")));
}
}
输出结果:
MD2散列值:bdf364fb82e5a89292949610730142a0
MD5散列值:a505a9d12b9aa595095bd800f63a528f
从上面三种方式示例结果对比可以看出,无论使用哪种方式,同一个字符串的MD值都是一样的。