6.2.3 实现
MD系列算法的实现是通过MessageDigest类来完成的,如果需要以流的处理方式完成消息摘要,则需要使用DigestInputStream和DigestOutputStream,有关Java API请读者朋友参照第3章内容。Java 6仅支持MD2和MD5两种算法,通过第三方加密组件包Bouncy Castle(详见第4章),可支持MD4算法。
MD系列算法支持如表6-1所示。
1.Sun
在Java 6中使用MD算法是很简单的。例如,要使用MD5算法对数据做消息摘要,可参考如下代码:
//初始化MessageDigest,并指定MD5算法
MessageDigest md=MessageDigest.getInstance("MD5");
//摘要处理
byte[]b=md.digest(data);
在上述代码中,data[]为待做消息摘要处理的数据,b[]是经过消息摘要处理后的摘要信息,也就是数字指纹。
Java 6支持MD2和MD5算法,如要使用MD2算法只需替换算法名即可,相关实现如代码清单6-1所示。
代码清单6-1 MD2和MD5算法实现
import java.security.MessageDigest;
/**
*MD消息摘要组件
*@author梁栋
*@version 1.0
*@since 1.0
*/
public abstract class MDCoder{
/**
*MD2消息摘要
*@param data待做摘要处理的数据
*@return byte[]消息摘要
*@throws Exception
*/
public static byte[]encodeMD2(byte[]data)throws Exception{
//初始化MessageDigest
MessageDigest md=MessageDigest.getInstance("MD2");
//执行消息摘要
return md.digest(data);
}
/**
*MD5消息摘要
*@param data待做摘要处理的数据
*@return byte[]消息摘要
*@throws Exception
*/
public static byte[]encodeMD5(byte[]data)throws Exception{
//初始化MessageDigest
MessageDigest md=MessageDigest.getInstance("MD5");
//执行消息摘要
return md.digest(data);
}
}
消息摘要的主要特点就是对同一段数据做多次摘要处理后,其摘要值完全一致。因此,我们通过必要两次消息摘要的结果来判别消息摘要是否一致,见代码清单6-2。
代码清单6-2 MD2和MD5算法实现测试用例
import static org.junit.Assert.*;
import org.junit.Test;
/**
*MD校验
*@author梁栋
*@version 1.0
*@since 1.0
*/
public class MDCoderTest{
/**
*测试MD2
*@throws Exception
*/
@Test
public final void testEncodeMD2()throws Exception{
String str="MD2消息摘要";
//获得摘要信息
byte[]data1=MDCoder.encodeMD2(str.getBytes());
byte[]data2=MDCoder.encodeMD2(str.getBytes());
//校验
assertArrayEquals(data1,data2);
}
/**
*测试MD5
*@throws Exception
*/
@Test
public final void testEncodeMD5()throws Exception{
String str="MD5消息摘要";
//获得摘要信息
byte[]data1=MDCoder.encodeMD5(str.getBytes());
byte[]data2=MDCoder.encodeMD5(str.getBytes());
//校验
assertArrayEquals(data1,data2);
}
}
测试结果自然不用说,一定完全一致!
2.Bouncy Castle
第三方加密组件包Bouncy Castle是对Java 6的友善补充,不仅提供了Base64算法的实现,更是弥补了Sun未能提供MD4算法的空白。
使用Bouncy Castle支持MD4算法比较简单的办法是将Bouncy Castle导入项目中,并在初始化MessageDigest前通过Security类的addProvider()方法加载第三方加密组件包提供者,我们以BouncyCastleProvider为例,提供MD4算法支持。要使用MD4算法对数据做消息摘要,可参考如下代码:
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
//省略
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
//初始化MessageDigest
MessageDigest md=MessageDigest.getInstance("MD4");
//执行消息摘要
md.digest(data);
有关于第三方加密组件包Bouncy Castle的配置,读者朋友可阅读第4章相关内容。
下面给出MD4算法实现,如代码清单6-3所示。
代码清单6-3 MD4算法实现
import java.security.MessageDigest;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
/**
*MD4消息摘要组件
*@author梁栋
*@version 1.0
*@since 1.0
*/
public abstract class MD4Coder{
/**
*MD4消息摘要
*@param data待做摘要处理的数据
*@return byte[]消息摘要
*@throws Exception
*/
public static byte[]encodeMD4(byte[]data)throws Exception{
//加入BouncyCastleProvider支持
Security.addProvider(new BouncyCastleProvider());
//初始化MessageDigest
MessageDigest md=MessageDigest.getInstance("MD4");
//执行消息摘要
return md.digest(data);
}
/**
*MD4消息摘要
*@param data待做摘要处理的数据
*@return String消息摘要
*@throws Exception
*/
public static String encodeMD4Hex(byte[]data)throws Exception{
//执行消息摘要
byte[]b=encodeMD4(data);
//做十六进制编码处理
return new String(Hex.encode(b));
}
}
上述代码提供了MD4算法实现(encode MD4()方法)的同时,加入了十六进制转换方法实现(encodeMD4Hex()方法)。关于十六进制编码转换相关API请阅读第4章内容。
我们完全可以将Bouncy Castle的相关支持融入其他MD算法实现中,提供完整的MD系列算法,并加入十六进制转换实现。
对于上述方法实现给出对应的测试用例,见代码清单6-4。
代码清单6-4 MD4算法实现测试用例
import static org.junit.Assert.*;
import org.junit.Test;
/**
*MD4校验
*@author梁栋
*@version 1.0
*@since 1.0
*/
public class MD4CoderTest{
/**
*测试MD4
*@throws Exception
*/
@Test
public final void testEncodeMD4()throws Exception{
String str="MD4消息摘要";
//获得摘要信息
byte[]data1=MD4Coder.encodeMD4(str.getBytes());
byte[]data2=MD4Coder.encodeMD4(str.getBytes());
//校验
assertArrayEquals(data1,data2);
}
/**
*测试MD4Hex
*@throws Exception
*/
@Test
public final void testEncodeMD4Hex()throws Exception{
String str="MD4Hex消息摘要";
//获得摘要信息
String data1=MD4Coder.encodeMD4Hex(str.getBytes());
String data2=MD4Coder.encodeMD4Hex(str.getBytes());
System.err.println("原文:\t"+str);
System.err.println("MD4Hex-1:\t"+data1);
System.err.println("MD4Hex-2:\t"+data2);
//校验
assertEquals(data1,data2);
}
}
我们来关注一下testEncodeMD4Hex()方法在控制台上的输出结果,如下所示:
原文:MD4Hex消息摘要
MD4Hex-1:6d46694b818e0bab41eab6783749f963
MD4Hex-2:6d46694b818e0bab41eab6783749f963
我们获得的两个摘要值都是32位的十六进制字符串,并且是一致的。
如果把十六进制转换的实现融入到其他MD算法的实现中,那岂不是相当完美?
3.Commons Codec
对于Commons Codec,想必读者朋友已经不再陌生。它实现了Base64算法,还提供了用于消息摘要的工具类—DigestUtils类(它位于org.apache.commons.codec.digest包中,请读者朋友阅读第4章相关内容。)。DigestUtils类是对Sun提供的MessageDigest类的一次封装,提供了MD5和SHA系列消息摘要算法的实现。
我们通过代码清单6-5来了解如何通过Commons Codec实现MD5算法。
代码清单6-5 MD5算法实现
import org.apache.commons.codec.digest.DigestUtils;
/**
*MD5消息摘要组件
*@author梁栋
*@version 1.0
*@since 1.0
*/
public abstract class MD5Coder{
/**
*MD5消息摘要
*@param data待做摘要处理的数据
*@return byte[]消息摘要
*@throws Exception
*/
public static byte[]encodeMD5(String data)throws Exception{
//执行消息摘要
return DigestUtils.md5(data);
}
/**
*MD5消息摘要
*@param data待做摘要处理的数据
*@return byte[]消息摘要
*@throws Exception
*/
public static String encodeMD5Hex(String data)throws Exception{
//执行消息摘要
return DigestUtils.md5Hex(data);
}
}
虽然Commons Codec只是对Sun提供的MD5算法实现做了一次简单的包装,但着实为我们使用该算法提供了不小的便利。相应的测试用例见代码清单6-6。
代码清单6-6 MD5算法实现测试用例
import static org.junit.Assert.*;
import org.junit.Test;
/**
*MD5校验
*@author梁栋
*@version 1.0
*@since 1.0
*/
public class MD5CoderTest{
/**
*测试MD5
*@throws Exception
*/
@Test
public final void testEncodeMD5()throws Exception{
String str="MD5消息摘要";
//获得摘要信息
byte[]data1=MD5Coder.encodeMD5(str);
byte[]data2=MD5Coder.encodeMD5(str);
//校验
assertArrayEquals(data1,data2);
}
/**
*测试MD5Hex
*@throws Exception
*/
@Test
public final void testEncodeMD5Hex()throws Exception{
String str="MD5Hex消息摘要";
//获得摘要信息
String data1=MD5Coder.encodeMD5Hex(str);
String data2=MD5Coder.encodeMD5Hex(str);
System.err.println("原文:\t"+str);
System.err.println("MD5Hex-1:\t"+data1);
System.err.println("MD5Hex-2:\t"+data2);
//校验
assertEquals(data1,data2);
}
}
我们来关注一下testEncodeMD5Hex()方法在控制台上的输出结果,如下所示:
原文:MD5Hex消息摘要
MD5Hex-1:4effa30b56b61156bcd005eb9968cc9b
MD5Hex-2:4effa30b56b61156bcd005eb9968cc9b
对同一个消息做MD5Hex处理后,得到的摘要值都是32位的十六进制字符串,并且是一致的。
4.三种实现方式的差异
三种实现方式以Sun提供的实现为基础,在算法支持上和方法易用性上提供了更好的扩展与支持。
❑Sun
Sun提供的算法实现较为底层,支持MD2和MD5两种算法。但缺少了相应的进制转换实现,不能将其字节数组形式的摘要信息转为十六进制字符串,这多少有点不方便。
❑Bouncy Castle
Bouncy Castle是对Sun的友善补充,提供了对MD4算法的支持。支持多种形式的参数,支持十六进制字符串形式的摘要信息。
❑Commons Codec
如果仅仅需要实现MD5算法的话,使用Commons Codec完成消息摘要处理是一个不错的选择。它支持多种形式的参数,支持十六进制字符串形式的摘要信息。
综上所述,我们可以根据需要有机地结合两种实现方式,满足系统的需要。如果只要求MD5算法支持,Commons Codec是首选;若要支持MD2或MD4算法,或者要求在此基础上获得十六进制编码结果,就让Sun和Bouncy Castle联姻。