7.2.3 实现

我们知道,密钥长度与安全性成正比,但Java 6仅支持56位密钥长度,作为补充,Bouncy Castle提供64位密钥长度支持。在此基础上配合不同的填充方式(如PKCS5Padding, PKCS7Padding),可显著提高加密系统的安全性。

有关DES算法的Java 6实现与Bouncy Castle实现细节如表7-1所示。

figure_0230_0053

请读者朋友注意DES算法的实现过程,其他对称加密算法与该算法实现相类似。

密钥的构建主要需要密钥生成器(KeyGenerator)完成生成操作,如下代码所示:


//实例化密钥生成器

KeyGenerator kg=KeyGenerator.getInstance("DES");

//初始化

kg.init(56);

//生成秘密密钥

SecretKey secretKey=kg.generateKey();

//获得密钥的二进制编码形式

byte[]b=secretKey.getEncoded();


字节数组b就是我们需要的秘密密钥字节数组形式。这便于我们将其存储在文件中,或以数据流的形式在网络中传输。

把密钥转化为二进制字节数组形式便于保存,但若我们要使用它需要将其转换为密钥对象,首先需要将二进制密钥转换为密钥材料对象(这里是DESKeySpec对象dks),再使用密钥工厂(SecretKeyFactory)生成密钥。实现代码如下所示:


//实例化DES密钥材料

DESKeySpec dks=new DESKeySpec(b);

//实例化秘密密钥工厂

SecretKeyFactory keyFactory=SecretKeyFactory.getInstance("DES");

//生成秘密密钥

SecretKey secretKey=keyFactory.generateSecret(dks);


对于DESede算法,则需要相应的DESedeKeySpec类替换DESKeySpec类来完成上述操作。

得到密钥对象后,我们就可以对数据做加密/解密处理,加密处理代码如下所示:


//实例化

Cipher cipher=Cipher.getInstance("DES");

//初始化,设置为加密模式

cipher.init(Cipher.ENCRYPT_MODE, secretKey);

//执行操作

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


上述实例化操作未指定工作模式及填充方式,我们将在后续内容中详述。

如果将上述初始化方法(init()方法)中的模式参数由“Cipher.ENCRYPT_MODE”改为“Cipher.DECRYPT_MODE”,则可作为解密处理。

Java 6提供了DES算法支持,但仅支持56位的密钥长度。我们知道密钥长度与加密强度成正比。我们可以使用Boucy Calstle提供密钥长度,由56位提高至64位。

接下来我们完成一套基于DES算法的密钥构建和加密/解密操作,如代码清单7-1所示。

代码清单7-1 DES算法实现


import java.security.Key;

import javax.crypto.Cipher;

import javax.crypto.KeyGenerator;

import javax.crypto.SecretKey;

import javax.crypto.SecretKeyFactory;

import javax.crypto.spec.DESKeySpec;

/**

*DES安全编码组件

*@author梁栋

*@version 1.0

*/

public abstract class DESCoder{

/**

*密钥算法<br>

*Java 6只支持56位密钥<br>

*Bouncy Castle支持64位密钥

*/

public static final String KEY_ALGORITHM="DES";

/**

*加密/解密算法/工作模式/填充方式

*/

public static final String CIPHER_ALGORITHM="DES/ECB/PKCS5Padding";

/**

*转换密钥

*@param key二进制密钥

*@return Key密钥

*@throws Exception

*/

private static Key toKey(byte[]key)throws Exception{

//实例化DES密钥材料

DESKeySpec dks=new DESKeySpec(key);

//实例化秘密密钥工厂

SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(KEY_ALGORITHM);

//生成秘密密钥

SecretKey secretKey=keyFactory.generateSecret(dks);

return secretKey;

}

/**

*解密

*@param data待解密数据

*@param key密钥

*@return byte[]解密数据

*@throws Exception

*/

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

//还原密钥

Key k=toKey(key);

//实例化

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);

//实例化

Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM);

//初始化,设置为加密模式

cipher.init(Cipher.ENCRYPT_MODE, k);

//执行操作

return cipher.doFinal(data);

}

/**

*生成密钥<br>

*Java 6只支持56位密钥<br>

*Bouncy Castle支持64位密钥<br>

*@return byte[]二进制密钥

*@throws Exception

*/

public static byte[]initKey()throws Exception{

/*

*实例化密钥生成器

*若要使用64位密钥注意替换

*将下述代码中的

*KeyGenerator.getInstance(CIPHER_ALGORITHM);

*替换为

*KeyGenerator.getInstance(CIPHER_ALGORITHM,"BC");

*/

KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM);

/*

*初始化密钥生成器

*若要使用64位密钥注意替换

*将下述代码kg.init(56);

*替换为kg.init(64);

*/

kg.init(56);

//生成秘密密钥

SecretKey secretKey=kg.generateKey();

//获得密钥的二进制编码形式

return secretKey.getEncoded();

}

}


请读者朋友注意上述代码中的生成密钥方法(initKey()),如果我们初始化密钥生成器时按如下方式实现,将获得一个默认长度的密钥:


kg.init(new SecureRandom());


Java 6仅仅提供了56位长度的密钥,因此上述方法将产生一个56位长度的密钥。

若我们想要构建一个64位密钥的DES算法,则需要按如下代码做替换实现:


KeyGenerator kg=KeyGenerator.getInstance(CIPHER_ALGORITHM,"BC");


在上述代码中,“BC”是Boucy Calstle安全提供者的缩写。

当然,你也可以使用如下方式替代上述代码:


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//省略

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

KeyGenerator kg=KeyGenerator.getInstance(CIPHER_ALGORITHM);


完成密钥生成器实例化操作后,需要注意密钥生成器初始化操作,如以下代码所示:


kg.init(64);


按上述代码实现方式,我们即可获得Boucy Calstle安全提供者提供的64位的DES算法密钥。

注意 密钥生成和加密/解密所使用的算法很可能是不同的。

在本文的DES算法实现中,密钥算法(变量KEY_ALGORITHM)是“DES”,而加密/解密算法(变量“CIPHER_ALGORITHM”)是“DES/ECB/PKCS5Padding”。这里的加密/解密算法中除了包含密钥算法(DES)外,还包含了工作模式(ECB)和填充方式(PKCS5Padding)。

如果密钥算法与加密/解密算法一致,则按默认工作模式和填充方式实现。

在实际应用中,密文通常以二进制数据传输/存储,而密钥通常会被转换为可见字符存储。如使用Base64编码或十六进制编码将不可见的二进制密钥转换为可见字符。当然,这里需要注意一点,若使用Base64编码,则编码后的信息将是原始信息长度的4/3倍(相关原理请阅读第5章)。

为了便于演示,作者将密文和密钥均使用Base64编码形式展示(我们使用Commons Codec完成Base64算法实现,相关内容请参考第5章)。测试用例实现如代码清单7-2所示。

代码清单7-2 DES算法实现测试用例


import static org.junit.Assert.*;

import org.apache.commons.codec.binary.Base64;

import org.junit.Test;

/**

*DES安全编码组件校验

*@author梁栋

*@version 1.0

*/

public class DESCoderTest{

/**

*测试

*@throws Exception

*/

@Test

public final void test()throws Exception{

String inputStr="DES";

byte[]inputData=inputStr.getBytes();

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

//初始化密钥

byte[]key=DESCoder.initKey();

System.err.println("密钥:\t"+Base64.encodeBase64String(key));

//加密

inputData=DESCoder.encrypt(inputData, key);

System.err.println("加密后:\t"+Base64.encodeBase64String(inputData));

//解密

byte[]outputData=DESCoder.decrypt(inputData, key);

String outputStr=new String(outputData);

System.err.println("解密后:\t"+outputStr);

//校验

assertEquals(inputStr, outputStr);

}

}


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


原文:DES

密钥:qA2oZBaKNOk=

加密后:QwCjNM5G8KM=

解密后:DES


加密/解密操作顺利完成!

这样的密钥短小精悍,便于记忆!作者与合作公司在构建加密通信模块时,通常会指定这样便于书写的密钥,并将其书写在合同书上发送给对方。这就是为什么要将密钥存储为Base64编码或十六进制编码的原因。

随着计算机的发展,密钥长度仅有56位的DES算法显得越来越不安全,虽然通过Bouncy Castle可将密钥长度增至64位,提高了其安全强度,但DES算法在设计上的漏洞已经不能通过单纯地增加密钥长度来弥补,这引发了对称加密算法研发竞赛!

DESede和AES算法正是这场竞赛中具有代表性的算法。