摘要算法:MD5及Java实现样例
MD5 = 第五代的消息摘要算法(Message Digest Algorithm)。
MD5 是一种不可逆的单向散列函数。
假设 y = f(x) ,则函数 f(x) 指代 MD5 函数, x 为待摘要消息(输入),y 为MD5散列值(输出)。
MD5 是一种散列函数,是一种摘要算法,和常见的 DES、RSA 等加解密算法完全是两个不同的概念。
DES、RSA 等加解密算法是可逆的,对正向加密的密文结果使用同一密码进行逆向解密可以获取明文输入,反之亦然。
MD5 却不同,你只能对其进行正向的哈希散列,获取一个散列值,但不能通过散列值获取原始输入内容。
MD5 的输入(待摘要消息)长度无要求。这里的“消息”其实是一系列的字节序列,可能包含不可读或不可打印字符。
MD5 的输出结果是一个 128 长度的 bit 串。(128 bit / 8 = 16字节)
MD5 有啥用呢?
我们对输入消息进行 MD5 运算,就像抽取计算消息的“指纹”一样,获取一个 bit 串,这个 bit 串就是输入消息的指纹。
前面提到过,“消息”是一组字节序列,当然也可以是任何文件,不论其格式、大小……
对一个文件进行 MD5 运算获取散列值,可以防止非法对文件进行篡改的行为。
比如,文件 libcurl.tar.gz 的 MD5 值(十六进制大写表示)为 E3B56A299BFAB49B5E64C8A01FE3ACA9。
E3B56A299BFAB49B5E64C8A01FE3ACA9 可以认为是文件 libcurl.tar.gz 的数字指纹。
当我下载完 libcurl.tar.gz 后 MD5 获取摘要发现下载的文件的 MD5 值为:6C139D7D77158948D0D390A78A166516。
和官方网站提供的 MD5 文件指纹不一样,我就知道下载时可能被人篡改了下载文件。
当然,也有可能是下载中出现传输异常导致某些 bit 不正确:
超大型游戏、软件等,当文件体量达到十几个G甚至几十个G以上时,官方会提供一份 MD5 之类的指纹值,用于校验下载过程中是否数据正常传输。
即使你对消息只进行 1 bit 的篡改,MD5 散列结果也是大不相同的。
Java 实现样例:
import java.security.MessageDigest;
public class MyMD5 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String msg = "0123456789abcdef";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(msg.getBytes());
byte []md5Bin = messageDigest.digest();
printBytes(md5Bin);
}
/**
* 十六进制打印字节数组
* @param b byte[]
*/
public static void printBytes(byte[] b)
{
for(int i=0;i<b.length;i++)
{
System.out.printf("%02X", b[i]);
}
System.out.println();
}
}
注:
代码中传入 messageDigest.update 的是一个字节数组。为啥呢?
因为消息入参就是一个字节序列,这个序列不一定可打印,不一定可读,就是一堆的字节组成的序列。
因此,你可以对任何一堆的字节序列进行 MD5 运算,包括文本、图片、音频文件等等,任何在计算机中可表示的!
messageDigest.digest 返回一个散列值字节序列,长度为 16 字节,共 128 比特。
我们将输出的散列值用十六进制大写编码输出,最后的结果就是:4032AF8D61035123906E58E067140CC5。
import java.security.MessageDigest;
public class MyMD5 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
{
String msg = "0123456789abcdef";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(msg.getBytes());
byte []md5Bin = messageDigest.digest();
printBytes(md5Bin);
}
{
String msg1 = "0123";
String msg2 = "4567";
String msg3 = "89ab";
String msg4 = "cdef";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(msg1.getBytes());
messageDigest.update(msg2.getBytes());
messageDigest.update(msg3.getBytes());
messageDigest.update(msg4.getBytes());
byte []md5Bin = messageDigest.digest();
printBytes(md5Bin);
}
{
String msg1 = "0123";
String msg2 = "4567";
String msg3 = "89ab";
String msg4 = "cdef";
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(msg1.getBytes());
messageDigest.update(msg2.getBytes());
messageDigest.update(msg3.getBytes());
byte []md5Bin = messageDigest.digest(msg4.getBytes());
printBytes(md5Bin);
}
}
/**
* 十六进制打印字节数组
* @param b byte[]
*/
public static void printBytes(byte[] b)
{
for(int i=0;i<b.length;i++)
{
System.out.printf("%02X", b[i]);
}
System.out.println();
}
}
上面的三块代码段相互等价,MD5 散列结果相同!
messageDigest.update 方法相当于是在待摘要字节序列后追加一些字节序列,可以多次调用,多次追加。
messageDigest.digest 方法是实际执行 MD5 算法接口,计算待摘要消息的散列值并返回之。
MD5摘要是单向不可逆的散列算法,不是加解密算法。
你可以通过一个人所有细节获取其特征指纹,但你不可能通过指纹逆向生产这个人的所有细节。