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所示。

figure_0236_0054

通过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算法实现就是一个不错的示例!