7.6.3 实现
Java 6和Bouncy Castle都提供了PBE系列算法的相关实现,差别在于对各种消息摘要算法和对称加密算法的组合。常用的消息摘要算法包括MD5和SHA算法,常用的对称加密算法包括DES、RC2等。PBE系列算法就是将这些算法进行合理组合,其密钥长度均以PBE具体算法中的对称加密算法密钥长度为准,其工作模式基本上为CBC模式。
有关PBE算法的Java 6和Bouncy Castle实现细节如表7-5所示。
PEB算法是实现过程中需要关注的环节,包括盐的初始化、密钥材料的转换以及加密/解密实现。
在初始化盐时,必须使用随机的方式构建盐,最终要得到一个8字节的字节数组。鉴于安全性要求,这里的随机数生成器只能使用SecureRandom类,如下所示:
//实例化安全随机数
SecureRandom random=new SecureRandom();
//产出盐
byte[]b=random.generateSeed(8);
字节数组b[]就是我们要的盐。
密钥材料转换部分不同于其他对称加密算法,这里使用的是PBEKeySpec类,如下所示:
//密钥材料转换
PBEKeySpec keySpec=new PBEKeySpec(password.toCharArray());
其他对称加密算法的密钥材料实现类的构造方法要求输入字节数组形式的变量,而PBEKeySpec类构造方法则要求输入字符数组变量。
为什么不是字符串(String)而是字符数组(char[])呢?这是因为字符串是可序列化的封装类,可在程序调用时被序列化到文件中,而字符数组只能以内存变量的形式保留在内存中。
在加密/解密实现时,需要注意使用参数材料PBEParameterSpec类,同时注意迭代次数。构建PBE参数材料后就可以转交给Cipher类完成加密/解密操作,如下所示:
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt, ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
本文以Java 6提供的PBEWITHMD5andDES算法为例,如代码清单7-9所示。
代码清单7-9 PBE算法实现
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
/**
*PBE安全编码组件
*@author梁栋
*@version 1.0
*/
public abstract class PBECoder{
/**
*Java 6支持以下任意一种算法
*PBEWithMD5AndDES
*PBEWithMD5AndTripleDES
*PBEWithSHA1AndDESede
*PBEWithSHA1AndRC2_40
*PBKDF2WithHmacSHA1
*/
public static final String ALGORITHM="PBEWITHMD5andDES";
/**
*迭代次数
*/
public static final int ITERATION_COUNT=100;
/**
*盐初始化<br>
*盐长度必须为8字节
*@return byte[]盐
*@throws Exception
*/
public static byte[]initSalt()throws Exception{
//实例化安全随机数
SecureRandom random=new SecureRandom();
//产出盐
return random.generateSeed(8);
}
/**
*转换密钥
*@param password密码
*@return Key密钥
*@throws Exception
*/
private static Key toKey(String password)throws Exception{
//密钥材料转换
PBEKeySpec keySpec=new PBEKeySpec(password.toCharArray());
//实例化
SecretKeyFactory keyFactory=SecretKeyFactory.getInstance(ALGORITHM);
//生成密钥
SecretKey secretKey=keyFactory.generateSecret(keySpec);
return secretKey;
}
/**
*加密
*@param data数据
*@param password密码
*@param salt盐
*@return byte[]加密数据
*@throws Exception
*/
public static byte[]encrypt(byte[]data, String password, byte[]salt)
throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt, ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
//执行操作
return cipher.doFinal(data);
}
/**
*解密
*@param data数据
*@param password密码
*@param salt盐
*@return byte[]解密数据
*@throws Exception
*/
public static byte[]decrypt(byte[]data, String password, byte[]salt)
throws Exception{
//转换密钥
Key key=toKey(password);
//实例化PBE参数材料
PBEParameterSpec paramSpec=new PBEParameterSpec(salt,
ITERATION_COUNT);
//实例化
Cipher cipher=Cipher.getInstance(ALGORITHM);
//初始化
cipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
//执行操作
return cipher.doFinal(data);
}
}
上述代码的测试用例如代码清单7-10所示。
代码清单7-10 PBE算法实现测试用例
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.junit.Test;
/**
*PBE校验
*@author梁栋
*@version 1.0
*/
public class PBECoderTest{
/**
*测试
*@throws Exception
*/
@Test
public void test()throws Exception{
String inputStr="PBE";
System.err.println("原文:"+inputStr);
byte[]input=inputStr.getBytes();
String pwd="snowolf@zlex.org";
System.err.println("密码:\t"+pwd);
//初始化盐
byte[]salt=PBECoder.initSalt();
System.err.println("盐:\t"+Base64.encodeBase64String(salt));
//加密
byte[]data=PBECoder.encrypt(input, pwd, salt);
System.err.println("加密后:\t"+Base64.encodeBase64String(data));
//解密
byte[]output=PBECoder.decrypt(data, pwd, salt);
String outputStr=new String(output);
System.err.println("解密后:\t"+outputStr);
//校验
assertEquals(inputStr, outputStr);
}
}
我们在控制台中得到如下信息:
原文:PBE
密码:snowolf@zlex.org
盐:qbjve/LfGIM=
加密后:NZQG0WfqAg4=
解密后:PBE