我正在尝试为AES,Serpent和TwoFish支持PBE。目前我可以使用BC生成Java中的AES PBEKey,如下所示:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWITHSHA256AND256BITAES-CBC-BC", provider);
PBEKeySpec pbeKeySpec = new PBEKeySpec("Password12".toCharArray());
SecretKey key = factory.generateSecret(pbeKeySpec);
但是我无法弄清楚如何为Serpent生成PBEKey,所以我认为它不可能开箱即用。我将如何实现这一目标?有什么钩子我可以注册我自己的SecretKeyFactory来处理Serpent键吗?
巧合的是,我注意到使用AES PBEKey(如上所述)使用Serpent / TwoFish加密/解密“工作”,但我不知道会产生什么影响。我可以直接使用AES PBEKey吗?
答案 0 :(得分:1)
在与PaŭloEbermann(上图)讨论后,我将以下解决方案放在一起。它为AES256生成一个PBE密钥,然后简单地将生成的密钥所需的字节数复制到一个新的SecretKeySpec()中,这允许我指定所需的算法和密钥长度。目前我正在使用密码并在每次加密调用时创建随机IV。我的假设是IV是不必要的,因为随机盐被应用于每个加密的消息,但我不是100%肯定所以我无论如何我添加了IV。我希望有人可以确认或否认这个假设,因为如果不需要IV,那么加密()的输出大小没有正当理由。理想情况下,我可以生成一个可变长度的PBEKey,没有算法关系(按照PKCS5),但看起来我受限于所选提供商提供的可用PBE方案中定义的密钥大小。因此,此实现必然使用BouncyCastle,因为我无法找到从标准JCE提供程序提供至少256位密钥的PBE方案。
/**
* parts of this code were copied from the StandardPBEByteEncryptor class from the Jasypt (www.jasypt.org) project
*/
public class PBESample {
private final String KEY_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
private final String MODE_PADDING = "/CBC/PKCS5Padding";
private final int DEFAULT_SALT_SIZE_BYTES = 16;
private final SecureRandom rand;
private final String passwd = "(Password){12}<.....>!";
public PBESample() throws Exception {
rand = SecureRandom.getInstance("SHA1PRNG");
}
private byte[] generateSalt(int size) {
byte[] salt = new byte[size];
rand.nextBytes(salt);
return salt;
}
private SecretKey generateKey(String algorithm, int keySize, byte[] salt) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException{
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray(), salt, 100000);
SecretKey tmpKey = factory.generateSecret(pbeKeySpec);
byte[] keyBytes = new byte[keySize / 8];
System.arraycopy(tmpKey.getEncoded(), 0, keyBytes, 0, keyBytes.length);
return new SecretKeySpec(keyBytes, algorithm);
}
private byte[] generateIV(Cipher cipher) {
byte[] iv = new byte[cipher.getBlockSize()];
rand.nextBytes(iv);
return iv;
}
private byte[] appendArrays(byte[] firstArray, byte[] secondArray) {
final byte[] result = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, result, 0, firstArray.length);
System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length);
return result;
}
public byte[] encrypt(String algorithm, int keySize, final byte[] message) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);
// The salt size for the chosen algorithm is set to be equal
// to the algorithm's block size (if it is a block algorithm).
int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
int algorithmBlockSize = cipher.getBlockSize();
if (algorithmBlockSize > 0) {
saltSizeBytes = algorithmBlockSize;
}
// Create salt
final byte[] salt = generateSalt(saltSizeBytes);
SecretKey key = generateKey(algorithm, keySize, salt);
// create a new IV for each encryption
final IvParameterSpec ivParamSpec = new IvParameterSpec(generateIV(cipher));
// Perform encryption using the Cipher
cipher.init(Cipher.ENCRYPT_MODE, key, ivParamSpec);
byte[] encryptedMessage = cipher.doFinal(message);
// append the IV and salt
encryptedMessage = appendArrays(ivParamSpec.getIV(), encryptedMessage);
encryptedMessage = appendArrays(salt, encryptedMessage);
return encryptedMessage;
}
public byte[] decrypt(String algorithm, int keySize, final byte[] encryptedMessage) throws Exception {
Cipher cipher = Cipher.getInstance(algorithm + MODE_PADDING);
// determine the salt size for the first layer of encryption
int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;
int algorithmBlockSize = cipher.getBlockSize();
if (algorithmBlockSize > 0) {
saltSizeBytes = algorithmBlockSize;
}
byte[] decryptedMessage = new byte[encryptedMessage.length];
System.arraycopy(encryptedMessage, 0, decryptedMessage, 0, encryptedMessage.length);
// extract the salt and IV from the incoming message
byte[] salt = null;
byte[] iv = null;
byte[] encryptedMessageKernel = null;
final int saltStart = 0;
final int saltSize = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
final int ivStart = (saltSizeBytes < decryptedMessage.length ? saltSizeBytes : decryptedMessage.length);
final int ivSize = cipher.getBlockSize();
final int encMesKernelStart = (saltSizeBytes + ivSize < decryptedMessage.length ? saltSizeBytes + ivSize : decryptedMessage.length);
final int encMesKernelSize = (saltSizeBytes + ivSize < decryptedMessage.length ? (decryptedMessage.length - saltSizeBytes - ivSize) : 0);
salt = new byte[saltSize];
iv = new byte[ivSize];
encryptedMessageKernel = new byte[encMesKernelSize];
System.arraycopy(decryptedMessage, saltStart, salt, 0, saltSize);
System.arraycopy(decryptedMessage, ivStart, iv, 0, ivSize);
System.arraycopy(decryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);
SecretKey key = generateKey(algorithm, keySize, salt);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
// Perform decryption using the Cipher
cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec);
decryptedMessage = cipher.doFinal(encryptedMessageKernel);
// Return the results
return decryptedMessage;
}
public static void main(String[] args) throws Exception {
// allow the use of the BC JCE
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final String message = "Secret Message";
PBESample engine = new PBESample();
byte[] encryptedMessage = engine.encrypt("AES", 128, message.getBytes());
byte[] decryptedMessage = engine.decrypt("AES", 128, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("AES OK");
}
encryptedMessage = engine.encrypt("Serpent", 256, message.getBytes());
decryptedMessage = engine.decrypt("Serpent", 256, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("Serpent OK");
}
encryptedMessage = engine.encrypt("TwoFish", 256, message.getBytes());
decryptedMessage = engine.decrypt("TwoFish", 256, encryptedMessage);
if (message.equals(new String(decryptedMessage))) {
System.out.println("TwoFish OK");
}
}
}