7.3.2 实现
对DES算法实现有一定了解后,DESede算法的实现就容易许多。除了将密钥材料实现类由DESKeySpec类转换为DESedeKeySpec类外,DESede算法与DES算法实现的主要差别在于算法、密钥长度两个方面:
❑算法:自然不用说,这是基本差别,只是DESede还有很多别名,如TripleDES和3DES指的都是DESede算法。
❑密钥长度:Java 6提供的DES算法实现支持56位密钥长度,加上Bouncy Castle相应实现可以支持到64位密钥长度。Java 6提供的DESede算法实现所支持的密钥长度支持为112位和168位,通过Bouncy Castle相应实现可支持密钥长度为128位和192位。DESede算法密钥长度恰恰是DES算法密钥长度的2倍或3倍。
有关DESede算法的Java 6实现与Bouncy Castle实现细节如表7-2所示。
通过7.2节的DES算法实现演示,相信读者对于如何实现DES算法,以及如何使用Bouncy Castle扩充密钥长度已经很了解了,本文将阐述如何使用Bouncy Castle扩充填充方式。
对于DESede算法的填充方式,Java 6提供了NoPadding、PKCS5Padding和ISO10126Padding共3种填充方式。
作者与合作方商定加密算法时,对方技术水准较高,要求使用PKCS7Padding填充方式,着实令作者犯难,好在这时作者发现了Bouncy Castle,才解决了这一技术难题。仔细研究,发现Bouncy Castle不仅支持PKCS7Padding这一种填充方式,还支持ISO10126d2Padding、X932Padding、ISO7816d4Padding和ZeroBytePadding共4种填充方式。
对于如何使用PKCS7Padding填充方式完成DESede算法构建的问题,我们在代码清单7-3中详述。
代码清单7-3 DESede算法实现
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
/**
*DESede安全编码组件
*@author梁栋
*@version 1.0
*/
public abstract class DESedeCoder{
/**
*密钥算法
*Java 6支持密钥长度为112位和168位
*Bouncy Castle支持密钥长度为128位和192位
*/
public static final String KEY_ALGORITHM="DESede";
/**
*加密/解密算法/工作模式/填充方式
*Java 6支持PKCS5Padding填充方式
*Bouncy Castle支持PKCS7Padding填充方式
*/
public static final String CIPHER_ALGORITHM="DESede/ECB/PKCS5Padding";
/**
*转换密钥
*@param key二进制密钥
*@return Key密钥
*@throws Exception
*/
private static Key toKey(byte[]key)throws Exception{
//实例化DES密钥材料
DESedeKeySpec dks=new DESedeKeySpec(key);
//实例化秘密密钥工厂
SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(KEY_ALGORITHM);
//生成秘密密钥
return keyFactory.generateSecret(dks);
}
/**
*解密
*@param data待解密数据
*@param key密钥
*@return byte[]解密数据
*@throws Exception
*/
public static byte[]decrypt(byte[]data, byte[]key)throws Exception{
//还原密钥
Key k=toKey(key);
/*
*实例化
*使用PKCS7Padding填充方式,按如下代码实现
*Cipher.getInstance(CIPHER_ALGORITHM,"BC");
*/
Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);
//初始化,设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, k);
//执行操作
return cipher.doFinal(data);
}
/**
*加密
*@param data待加密数据
*@param key密钥
*@return byte[]加密数据
*@throws Exception
*/
public static byte[]encrypt(byte[]data, byte[]key)throws Exception{
//还原密钥
Key k=toKey(key);
/*
*实例化
*使用PKCS7Padding填充方式,按如下代码实现
*Cipher.getInstance(CIPHER_ALGORITHM,"BC");
*/
Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);
//初始化,设置为加密模式
cipher.init(Cipher.ENCRYPT_MODE, k);
//执行操作
return cipher.doFinal(data);
}
/**
*生成密钥<br>
*@return byte[]二进制密钥
*@throws Exception
*/
public static byte[]initKey()throws Exception{
/*
*实例化
*使用128位或192位长度密钥,按如下代码实现
*KeyGenerator.getInstance(KEY_ALGORITHM,"BC");
*/
KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM);
/*
*初始化
*Java 6支持密钥长度为112位和168位
*若使用128位或192位长度密钥,按如下代码实现
*kg.init(128);
*或
*kg.init(192);
*/
kg.init(168);
//生成秘密密钥
SecretKey secretKey=kg.generateKey();
//获得密钥的二进制编码形式
return secretKey.getEncoded();
}
}
上述代码与DES算法实现如出一辙。这要感谢Sun提供的JCE架构,它提供了统一的加密算法调用模式。我们注意到,这里的密钥材料实现类由DESKeySpec类改为DESedeKeySpec类,这是为DESede算法量身定做的密钥材料实现类。
除了密钥材料实现类的变化,还要注意密钥长度的区别。
Java 6支持112位和168位密钥长度,注意初始化时显式调用如下代码:
kg.init(168);
DESede算法密钥生成器的密钥长度初始化默认值为168位,若使用无参初始化方法,一样会产生一个168位的DESede算法密钥。
如果要使用128位或192位长度的密钥,需要在实例化密钥生成器对象时就指定Bouncy Castle作为该算法的提供者。我们以初始化192位长度密钥为例,按如下代码实现:
KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM,"BC");
kg.init(192);
上述密钥长度初始化实现与DES算法实现无差别,这里要注意的是填充方式扩展。
注意代码中的变量“CIPHER_ALGORITHM”,当前指定的填充方式是PKCS5Padding,若我们使用PKCS7Padding填充方式除了对该变量做调整外,还需要调整Cipher对象cipher实例化代码,按如下方式实现:
Cipher.getInstance(CIPHER_ALGORITHM,"BC");
这样我们就能获得相应填充方式下的加密/解密实现了。
关于DESede算法相应的测试用例与DES算法的测试用例基本没有差别,如代码清单7-4所示。
代码清单7-4 DESede算法实现测试用例
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
/**
*DESede安全编码组件校验
*@author梁栋
*@version 1.0
*/
public class DESedeCoderTest{
/**
*测试
*@throws Exception
*/
@Test
public final void test()throws Exception{
String inputStr="DESede";
byte[]inputData=inputStr.getBytes();
System.err.println("原文:\t"+inputStr);
//初始化密钥
byte[]key=DESedeCoder.initKey();
System.err.println("密钥:\t"+Base64.encodeBase64String(key));
//加密
inputData=DESedeCoder.encrypt(inputData, key);
System.err.println("加密后:\t"+Base64.encodeBase64String(inputData));
//解密
byte[]outputData=DESedeCoder.decrypt(inputData, key);
String outputStr=new String(outputData);
System.err.println("解密后:\t"+outputStr);
//校验
assertEquals(inputStr, outputStr);
}
}
我们可以从控制台中获得信息中明显感受到密钥长度的增加。控制台输出信息如下所示:
原文:DESede
密钥:N8jTp6RuZxkjJea2XVvquV5YegHQ31cV
加密后:touXuJw8vrc=
解密后:DESede
仔细研究过Java API的读者朋友也许会对上述代码中的密钥材料实现类的变化敏感一些,Java API中仅仅提供了DES、DESede和PBE共3种对称加密算法密钥材料实现类。那么,其他算法如何还原密钥呢?AES算法实现就是一个不错的示例!