6.3.3 实现

在Java 6中,MessageDigest类支持MD算法的同时也支持SHA算法,几乎涵盖了我们目前所知的全部SHA系列算法,主要包含SHA-1、SHA-256、SHA-384和SHA-512四种算法。通过第三方加密组件包Bouncy Castle(详见本书附录),可支持SHA-224算法。

SHA系列算法支持如表6-2所示。

figure_0182_0043

1.Sun

在Java 6中,“SHA”是“SHA-1”的简称,两种算法名称等同。如果要使用SHA-1算法对数据做消息摘要,可参考如下代码:


//初始化MessageDigest,并指定SHA算法

MessageDigest md=MessageDigest.getInstance("SHA");

//摘要处理

byte[]b=md.digest(data);


在上述代码中,data[]为待做消息摘要处理的数据,b[]是经过消息摘要处理后的摘要信息。

Java 6支持SHA-1、SHA-256、SHA-384和SHA-512四种算法,实现方式如代码清单6-7所示。

代码清单6-7 SHA算法实现1


import java.security.MessageDigest;

/**

*SHA消息摘要组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class SHACoder{

/**

*SHA-1消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA(byte[]data)throws Exception{

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA");

//执行消息摘要

return md.digest(data);

}

/**

*SHA-256消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA256(byte[]data)throws Exception{

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA-256");

//执行消息摘要

return md.digest(data);

}

/**

*SHA-384消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA384(byte[]data)throws Exception{

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA-384");

//执行消息摘要

return md.digest(data);

}

/**

*SHA-512消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA512(byte[]data)throws Exception{

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA-512");

//执行消息摘要

return md.digest(data);

}

}


对上述代码做测试用例,如代码清单6-8所示。

代码清单6-8 SHA算法实现1测试用例


import static org.junit.Assert.*;

import org.junit.Test;

/**

*SHA校验

*@author梁栋

*@version 1.0

*@since 1.0

*/

public class SHACoderTest{

/**

*测试SHA-1

*@throws Exception

*/

@Test

public final void testEncodeSHA()throws Exception{

String str="SHA1消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA(str.getBytes());

byte[]data2=SHACoder.encodeSHA(str.getBytes());

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-256

*@throws Exception

*/

@Test

public final void testEncodeSHA256()throws Exception{

String str="SHA256消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA256(str.getBytes());

byte[]data2=SHACoder.encodeSHA256(str.getBytes());

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-384

*@throws Exception

*/

@Test

public final void testEncodeSHA384()throws Exception{

String str="SHA384消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA384(str.getBytes());

byte[]data2=SHACoder.encodeSHA384(str.getBytes());

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-512

*@throws Exception

*/

@Test

public final void testEncodeSHA512()throws Exception{

String str="SHA512消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA512(str.getBytes());

byte[]data2=SHACoder.encodeSHA512(str.getBytes());

//校验

assertArrayEquals(data1,data2);

}

}


作者和读者朋友一定都会这样想:如果能增加十六进制转换的方法,那就更加方便了!

2.Bouncy Castle

Bouncy Castle不仅支持MD4算法,同时也支持SHA-224算法。

比较简单的实现方式如下所示:


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA-224");


有关第三方加密组件包Bouncy Castle,读者可参考第3章和第4章相关内容。

对于SHA算法,我们仅仅需要补充SHA-244算法实现,如代码清单6-9所示。

代码清单6-9 SHA224算法实现


import java.security.MessageDigest;

import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.util.encoders.Hex;

/**

*SHA-224消息摘要组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class SHA224Coder{

/**

*SHA-224消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA224(byte[]data)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//初始化MessageDigest

MessageDigest md=MessageDigest.getInstance("SHA-224");

//执行消息摘要

return md.digest(data);

}

/**

*SHA-224消息摘要

*@param data待做摘要处理的数据

*@return String十六进制消息摘要

*@throws Exception

*/

public static String encodeSHA224Hex(byte[]data)throws Exception{

//执行消息摘要

byte[]b=encodeSHA224(data);

//做十六进制编码处理

return new String(Hex.encode(b));

}

}


对于上述实现做测试用例也相当简单,如代码清单6-10所示。

代码清单6-10 SHA224算法实现测试用例


import static org.junit.Assert.*;

import org.junit.Test;

/**

*SHA-224校验

*@author梁栋

*@version 1.0

*@since 1.0

*/

public class SHA224CoderTest{

/**

*测试SHA-224

*@throws Exception

*/

@Test

public final void testEncodeSHA224()throws Exception{

String str="SHA224消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA224(str.getBytes());

byte[]data2=SHACoder.encodeSHA224(str.getBytes());

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-224

*@throws Exception

*/

@Test

public final void testEncodeSHA224Hex()throws Exception{

String str="SHA224消息摘要";

//获得摘要信息

String data1=SHA224Coder.encodeSHA224Hex(str.getBytes());

String data2=SHA224Coder.encodeSHA224Hex(str.getBytes());

//校验

assertEquals(data1,data2);

}

}


对于其他SHA算法,我们完全可以对其加工,加入十六进制编码转换实现。我们来关注控制台输出的信息,如下所示:


原文:SHA224Hex消息摘要

SHA224Hex-1:fd0c016cc823d094aafc4d64665837df8ffa1d1712f58e5c44fd20d2

SHA224Hex-2:fd0c016cc823d094aafc4d64665837df8ffa1d1712f58e5c44fd20d2


在上述内容中,我们得到了28位的摘要信息,换算成二进制正好是224位。

3.Commons Codec

DigestUtils类除了MD5算法外,还支持多种SHA系列算法,涵盖了Java 6所支持的全部SHA算法。有关DigestUtils类相关SHA算法支持API,读者可阅读第4章内容。

Commons Codec与Sun所提供的SHA算法实现在本质上毫无差别,关键在于Commons Codec提供了更为方便的实现。我们对上述SHA算法实现做了调整,相关实现如代码清单6-11所示。

代码清单6-11 SHA算法实现2


import org.apache.commons.codec.digest.DigestUtils;

/**

*SHA消息摘要组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class SHACoder{

/**

*SHA消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha(data);

}

/**

*SHAHex消息摘要

*@param data待做摘要处理的数据

*@return String消息摘要

*@throws Exception

*/

public static String encodeSHAHex(String data)throws Exception{

//执行消息摘要

return DigestUtils.shaHex(data);

}

/**

*SHA256消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA256(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha256(data);

}

/**

*SHA256Hex消息摘要

*@param data待做摘要处理的数据

*@return String消息摘要

*@throws Exception

*/

public static String encodeSHA256Hex(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha256Hex(data);

}

/**

*SHA384消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA384(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha384(data);

}

/**

*SHA384Hex消息摘要

*@param data待做摘要处理的数据

*@return String消息摘要

*@throws Exception

*/

public static String encodeSHA384Hex(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha384Hex(data);

}

/**

*SHA512Hex消息摘要

*@param data待做摘要处理的数据

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeSHA512(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha512(data);

}

/**

*SHA512Hex消息摘要

*@param data待做摘要处理的数据

*@return String消息摘要

*@throws Exception

*/

public static String encodeSHA512Hex(String data)throws Exception{

//执行消息摘要

return DigestUtils.sha512Hex(data);

}

}


对于上述实现做测试用例,如代码清单6-12所示。

代码清单6-12 SHA算法实现2测试用例


import static org.junit.Assert.*;

import org.junit.Test;

/**

*SHAHex校验

*@author梁栋

*@version 1.0

*@since 1.0

*/

public class SHACoderTest{

/**

*测试SHA-1

*@throws Exception

*/

@Test

public final void testEncodeSHA()throws Exception{

String str="SHA1消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA(str);

byte[]data2=SHACoder.encodeSHA(str);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-1Hex

*@throws Exception

*/

@Test

public final void testEncodeSHAHex()throws Exception{

String str="SHA-1Hex消息摘要";

//获得摘要信息

String data1=SHACoder.encodeSHAHex(str);

String data2=SHACoder.encodeSHAHex(str);

System.err.println("原文:\t"+str);

System.err.println("SHA1Hex-1:\t"+data1);

System.err.println("SHA1Hex-2:\t"+data2);

//校验

assertEquals(data1,data2);

}

/**

*测试SHA-256

*@throws Exception

*/

@Test

public final void testEncodeSHA256()throws Exception{

String str="SHA256消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA256(str);

byte[]data2=SHACoder.encodeSHA256(str);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-256Hex

*@throws Exception

*/

@Test

public final void testEncodeSHA256Hex()throws Exception{

String str="SHA256Hex消息摘要";

//获得摘要信息

String data1=SHACoder.encodeSHA256Hex(str);

String data2=SHACoder.encodeSHA256Hex(str);

System.err.println("原文:\t"+str);

System.err.println("SHA256Hex-1:\t"+data1);

System.err.println("SHA256Hex-2:\t"+data2);

//校验

assertEquals(data1,data2);

}

/**

*测试SHA-384

*@throws Exception

*/

@Test

public final void testEncodeSHA384()throws Exception{

String str="SHA384消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA384(str);

byte[]data2=SHACoder.encodeSHA384(str);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-384Hex

*@throws Exception

*/

@Test

public final void testEncodeSHA384Hex()throws Exception{

String str="SHA384Hex消息摘要";

//获得摘要信息

String data1=SHACoder.encodeSHA384Hex(str);

String data2=SHACoder.encodeSHA384Hex(str);

System.err.println("原文:\t"+str);

System.err.println("SHA384Hex-1:\t"+data1);

System.err.println("SHA384Hex-2:\t"+data2);

//校验

assertEquals(data1,data2);

}

/**

*测试SHA-512

*@throws Exception

*/

@Test

public final void testEncodeSHA512()throws Exception{

String str="SHA512消息摘要";

//获得摘要信息

byte[]data1=SHACoder.encodeSHA512(str);

byte[]data2=SHACoder.encodeSHA512(str);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试SHA-512Hex

*@throws Exception

*/

@Test

public final void testEncodeSHA512Hex()throws Exception{

String str="SHA512Hex消息摘要";

//获得摘要信息

String data1=SHACoder.encodeSHA512Hex(str);

String data2=SHACoder.encodeSHA512Hex(str);

System.err.println("原文:\t"+str);

System.err.println("SHA512Hex-1:\t"+data1);

System.err.println("SHA512Hex-2:\t"+data2);

//校验

assertEquals(data1,data2);

}

}


在控制台中,我们可以清晰地观察到4种SHA算法的摘要信息长度有所不同。

❑SHA-1


原文:SHA-1Hex消息摘要

SHA1Hex-1:9a4135598d12e61f54d5d790887cf47721cbdd2c

SHA1Hex-2:9a4135598d12e61f54d5d790887cf47721cbdd2c

SHA-1算法的摘要信息是一个40位的十六进制字符串,换算成二进制正好是160位。


❑SHA-256


原文:SHA256Hex消息摘要

SHA256Hex-1:e001852cd945459a14ecd83ec3f9c73b94e02ec12ec8afbf764a98719acce777 SHA256Hex-2:

e001852cd945459a14ecd83ec3f9c73b94e02ec12ec8afbf764a98719acce777

SHA-256算法的摘要信息是一个64位的十六进制字符串,换算成二进制正好是256位。


❑SHA-384和SHA-512


原文:SHA384Hex消息摘要

SHA384Hex-1:

13d87b76ee7a14fcbb7d96c90ed430b8793cdd75afa10700a402ebdce463a29302ce36b19db4f30f e170399897f191ed

SHA384Hex-2:

13d87b76ee7a14fcbb7d96c90ed430b8793cdd75afa10700a402ebdce463a29302ce36b19db4f30f e170399897f191ed

原文:SHA512Hex消息摘要

SHA512Hex-1:

0ecc3742f5ca400c78f84d4bf37a7e34aff371f879d773cab40fe1cd4cb19b6cf2bc598e4bc5a384

808b9b15ef19ff59db4793f0f6b3815bdfd6152208e26756

SHA512Hex-2:

0ecc3742f5ca400c78f84d4bf37a7e34aff371f879d773cab40fe1cd4cb19b6cf2bc598e4bc5a384

808b9b15ef19ff59db4793f0f6b3815bdfd6152208e26756


很显然,SHA-384和SHA-512算法的摘要信息没有任何悬念,分别对应384位和512位的二进制数。

消息摘要长度与安全强度成正比。从这一点来说,SHA系列算法比MD系列算法更具优势。MD系列算法仅有128位,而SHA算法则可以从160位扩充到512位,更具安全性。

4.三种实现方式的差异

三种实现方式以Sun提供的实现为基础,在算法支持上和方法易用性上提供了更好的扩展与支持。

❑Sun

由于Sun提供了较为底层的SHA算法实现,如SHA-1、SHA-256、SHA-384和SHA-512四种算法,但缺少了对应的进制转换实现,多少有些遗憾。

❑Bouncy Castle

Bouncy Castle是对Sun的友善补充,提供了对SHA-224算法的支持,支持十六进制字符串形式的摘要信息,相当方便。

❑Commons Codec

Commons Codec对Sun提供的SHA算法做了包装,支持多种形式的参数,支持十六进制字符串形式的摘要信息,相当方便。

综上所述,若在应用中使用SHA系列算法,且不要求对SHA-224算法提供支持,Commons Codec是更为合适的选择。如果只是需要在Sun原有SHA系列算法支持的基础上加入十六进制编码转换,Bouncy Castle和Commons Codec都是不错的选择。