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所示。
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更为恰当。