6.4.3 实现

在Java 6中,MAC系列算法需要通过Mac类提供支持。有MAC算法相关的API请读者朋友参照第3章内容。

Java 6中仅仅提供了HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512四种算法,而第三方加密组件包Bouncy Castle补充了HmacMD2、HmacMD4和HmacSHA224三种算法支持。有关Bouncy Castle的内容请见附录。

MAC系列算法支持如表6-3所示。

figure_0196_0046

1.Sun

Mac算法是带有密钥的消息摘要算法,所以实现起来要分两步:

1)构建密钥。

2)执行消息摘要。

对应上述步骤,以HmacMD5算法为例,构建密钥代码如下所示:


//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacMD5");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

byte[]key=secretKey.getEncoded();


上述代码中的字节数组key[]就是我们构造的密钥。

我们需要对其还原,得到密钥,参考如下代码:


//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacMD5");


获得密钥后,我们就可以按如下代码做消息摘要了,参考如下代码:


//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

byte[]data=mac.doFinal(data);


上述代码中的字节数组data[]就是我们获得的摘要结果了。

Sun在Java 6中提供了HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和HmacSHA512四种算法支持,算法实现如代码清单6-13所示。

代码清单6-13 MAC算法实现1


import javax.crypto.KeyGenerator;

import javax.crypto.Mac;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

/**

*MAC消息摘要组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class MACCoder{

/**

*初始化HmacMD5密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacMD5Key()throws Exception{

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacMD5");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacMD5消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacMD5(byte[]data, byte[]key)

throws Exception{

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacMD5");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*初始化HmacSHA1密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacSHAKey()throws Exception{

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA1");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacSHA1消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacSHA(byte[]data, byte[]key)

throws Exception{

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacSHA1");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*初始化HmacSHA256密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacSHA256Key()throws Exception{

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA256");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacSHA256消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacSHA256(byte[]data, byte[]key)

throws Exception{

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacSHA256");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*初始化HmacSHA384密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacSHA384Key()throws Exception{

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA384");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacSHA384消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacSHA384(byte[]data, byte[]key)throws Exception{

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacSHA384");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*初始化HmacSHA512密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacSHA512Key()throws Exception{

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA512");

//产生密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacSHA512消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacSHA512(byte[]data, byte[]key)throws Exception{

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacSHA512");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

}


对于上述代码的测试较为简单,如代码清单6-14所示。

代码清单6-14 MAC算法实现1测试用例


import static org.junit.Assert.*;

import org.junit.Test;

/**

*MAC校验

*@author梁栋

*@version 1.0

*@since 1.0

*/

public class MACCoderTest{

/**

*测试HmacMD5

*@throws Exception

*/

@Test

public final void testEncodeHmacMD5()throws Exception{

String str="HmacMD5消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacMD5Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacMD5(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacMD5(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacSHA1

*@throws Exception

*/

@Test

public final void testEncodeHmacSHA()throws Exception{

String str="HmacSHA1消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacSHAKey();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacSHA(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacSHA(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacSHA256

*@throws Exception

*/

@Test

public final void testEncodeHmacSHA256()throws Exception{

String str="HmacSHA256消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacSHA256Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacSHA256(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacSHA256(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacSHA384

*@throws Exception

*/

@Test

public final void testEncodeHmacSHA384()throws Exception{

String str="HmacSHA384消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacSHA384Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacSHA384(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacSHA384(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacSHA512

*@throws Exception

*/

@Test

public final void testEncodeHmacSHA512()throws Exception{

String str="HmacSHA512消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacSHA512Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacSHA512(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacSHA512(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

}


上述代码实现唯一不足之处在于缺少了十六进制编码转换实现,可以使用Bouncy Castle或Commons Codec的十六进制编码转换实现来做补充。

2.Bouncy Castle

第三方加密组件包Bouncy Castle作为补充,提供了HmacMD2、HmacMD4和HmacSHA224三种算法支持,弥补了Sun在Java 6中未能提供相关算法实现的缺憾。

比较简单的使用方式是将其jar包导入项目中,在做初始化密钥和消息摘要前,执行如下代码:


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//省略

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());


对于上述算法的实现,如代码清单6-15所示。

代码清单6-15 MAC算法实现2


import java.security.Security;

import javax.crypto.KeyGenerator;

import javax.crypto.Mac;

import javax.crypto.SecretKey;

import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.util.encoders.Hex;

/**

*MAC消息摘要组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class MACCoder{

/**

*初始化HmacMD2密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacMD2Key()throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacMD2");

//产生秘密密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacMD2消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacMD2(byte[]data, byte[]key)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacMD2");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*HmacMD2Hex消息摘要

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

*@param String密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static String encodeHmacMD2Hex(byte[]data, byte[]key)throws Exception{

//执行消息摘要

byte[]b=encodeHmacMD2(data, key);

//做十六进制转换

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

}

/**

*初始化HmacMD4密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacMD4Key()throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacMD4");

//产生秘密密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacMD4消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacMD4(byte[]data, byte[]key)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacMD4");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*HmacMD4Hex消息摘要

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

*@param key密钥

*@return String消息摘要

*@throws Exception

*/

public static String encodeHmacMD4Hex(byte[]data, byte[]key)throws Exception{

//执行消息摘要

byte[]b=encodeHmacMD4(data, key);

//做十六进制转换

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

}

/**

*初始化HmacSHA224密钥

*@return byte[]密钥

*@throws Exception

*/

public static byte[]initHmacSHA224Key()throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//初始化KeyGenerator

KeyGenerator keyGenerator=KeyGenerator.getInstance("HmacSHA224");

//产生秘密密钥

SecretKey secretKey=keyGenerator.generateKey();

//获得密钥

return secretKey.getEncoded();

}

/**

*HmacSHA224消息摘要

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

*@param key密钥

*@return byte[]消息摘要

*@throws Exception

*/

public static byte[]encodeHmacSHA224(byte[]data, byte[]key)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//还原密钥

SecretKey secretKey=new SecretKeySpec(key,"HmacSHA224");

//实例化Mac

Mac mac=Mac.getInstance(secretKey.getAlgorithm());

//初始化Mac

mac.init(secretKey);

//执行消息摘要

return mac.doFinal(data);

}

/**

*HmacSHA224Hex消息摘要

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

*@param key密钥

*@return String消息摘要

*@throws Exception

*/

public static String encodeHmacSHA224Hex(byte[]data, byte[]key)throws Exception{

//执行消息摘要

byte[]b=encodeHmacSHA224(data, key);

//做十六进制转换

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

}

}


对于上述实现做相关测试,如代码清单6-16所示。

代码清单6-16 MAC算法实现2测试用例


import static org.junit.Assert.*;

import org.junit.Test;

/**

*MAC校验

*@author梁栋

*@version 1.0

*@since 1.0

*/

public class MACCoderTest{

/**

*测试HmacMD2

*@throws Exception

*/

@Test

public final void testEncodeHmacMD2()throws Exception{

String str="HmacMD2消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacMD2Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacMD2(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacMD2(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacMD4

*@throws Exception

*/

@Test

public final void testEncodeHmacMD4()throws Exception{

String str="HmacMD4消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacMD4Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacMD4(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacMD4(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

/**

*测试HmacSHA224

*@throws Exception

*/

@Test

public final void testEncodeHmacSHA224()throws Exception{

String str="HmacSHA224消息摘要";

//初始化密钥

byte[]key=MACCoder.initHmacSHA224Key();

//获得摘要信息

byte[]data1=MACCoder.encodeHmacSHA224(str.getBytes(),key);

byte[]data2=MACCoder.encodeHmacSHA224(str.getBytes(),key);

//校验

assertArrayEquals(data1,data2);

}

}


观察控制台输出的信息,如下所示:


原文:HmacMD2Hex消息摘要

HmacMD2Hex-1:bf5fa06c2c4855825a23ee08206c892f

HmacMD2Hex-2:bf5fa06c2c4855825a23ee08206c892f

原文:HmacMD4Hex消息摘要

HmacMD4Hex-1:c6ac3ec24690011bdfad9ce2e7aed1e7

HmacMD4Hex-2:c6ac3ec24690011bdfad9ce2e7aed1e7

原文:HmacSHA224Hex消息摘要

HmacSHA224Hex-1:54786b4722f72a2599ebb1bba3732bca9f0353392e51729bfca91c78

HmacSHA224Hex-2:54786b4722f72a2599ebb1bba3732bca9f0353392e51729bfca91c78


我们可以清楚地看到,经HmacMD2Hex和HmacMD4Hex处理后得到的字符串是一个32位的十六进制字符串,换算成二进制正好是128位,也就是MD系列算法做消息摘要处理后得到的摘要值长度。同理,HmacSHA224Hex处理得到的消息摘要值与SHA-224算法做消息摘要处理后得到的摘要值长度相同。

3.两种实现方式的差异

两种实现方式以Sun提供的实现为基础,在算法支持上提供了更好的扩展。

❑Sun

提供了基本的算法支持,如HmacMD5、HmacSHA1、HmacSHA256、HmacSHA384和Hmac512五种算法支持。

❑Bouncy Castle

Bouncy Castle在Sun的基础上添加了对HmacMD2、HmacMD4和HmacSHA224三种算法的支持,同时支持十六进制编码。

综上所述,根据需求恰当使用上述实现是很有必要的。如果需要HmacMD2、HmacMD4或HmacSHA224算法支持,可以使用Bouncy Castle做相关实现。如果还需要对摘要结果做十六进制编码,则使用Bouncy Castle更为恰当。