我目前正在向JCE注册BouncyCastleProvider并在我的Java Runtime中安装无限强度策略。因为这迫使我将运行时与产品一起发布,所以我想切换到BouncyCastle的轻量级API。
不幸的是,轻量级API出现的文档很少,所以我正努力设置“PBEWITHSHA256AND256BITAES-CBC-BC”算法的完全等价。
在深入研究BouncyCastle的源代码和searching SO之后,我编写了一个小的测试工具,它没有返回预期的内容:
public class AESCipherTest {
private static final String data = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, " +
"sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, " +
"sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet " +
"clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem " +
"ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor " +
"invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et " +
"accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata " +
"sanctus est Lorem ipsum dolor sit amet.";
private static final String password = "MySuperStrongPassword";
private static final byte[] salt = "MakeItSpicey".getBytes();
private static final String jce_algorithm = "PBEWITHSHA256AND256BITAES-CBC-BC";
public static void main(String[] args) throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
File file = new File("D:\\test.dat");
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
OutputStream cos = encryptWithJCE(fos, password);
cos.write(data.getBytes());
cos.flush();
cos.close();
FileInputStream fis = new FileInputStream(file);
InputStream cis = decryptWithBCLWA(fis, password);
byte[] readByteData = new byte[data.getBytes().length];
cis.read(readByteData);
cis.close();
String readData = new String(readByteData);
System.out.println(readData);
}
public static OutputStream encryptWithJCE(OutputStream os, String password) throws Exception {
javax.crypto.spec.PBEParameterSpec pbeParamSpec = new javax.crypto.spec.PBEParameterSpec(salt, 20);
javax.crypto.spec.PBEKeySpec pbeKeySpec = new javax.crypto.spec.PBEKeySpec(password.toCharArray());
javax.crypto.SecretKeyFactory secretKeyFactory = javax.crypto.SecretKeyFactory.getInstance(jce_algorithm);
javax.crypto.SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance(jce_algorithm);
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
OutputStream fOutputStream = new FilterOutputStream(
new BufferedOutputStream(os)) {
@Override
public void close() throws IOException {
// do nothing
}
};
return new javax.crypto.CipherOutputStream(fOutputStream, cipher);
}
public static InputStream decryptWithBCLWA(InputStream inputStream, String password) throws Exception {
org.bouncycastle.crypto.generators.PKCS12ParametersGenerator generator = new org.bouncycastle.crypto.generators.PKCS12ParametersGenerator(new org.bouncycastle.crypto.digests.SHA256Digest());
char[] passwordChars = password.toCharArray();
byte[] pkcs12PasswordToBytes = org.bouncycastle.crypto.PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
generator.init(pkcs12PasswordToBytes, salt, 20);
org.bouncycastle.crypto.modes.CBCBlockCipher cbcBlockCipher = new org.bouncycastle.crypto.modes.CBCBlockCipher(new org.bouncycastle.crypto.engines.AESEngine());
org.bouncycastle.crypto.params.ParametersWithIV cipherParameters = (org.bouncycastle.crypto.params.ParametersWithIV) generator.generateDerivedParameters(256, 128);
cbcBlockCipher.init(false, cipherParameters);
org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher aesCipher = new org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher(cbcBlockCipher, new org.bouncycastle.crypto.paddings.PKCS7Padding());
return new org.bouncycastle.crypto.io.CipherInputStream(inputStream, aesCipher);
}}
我错过了什么?非常感谢任何帮助!
还有一段旧代码需要从JCE过渡到BC轻量级API:
byte[] keyBytes = ... // password in bytes
javax.crypto.spec.SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
javax.crypto.Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new javax.crypto.CipherInputStream(in, cipher);
我知道将AES密钥与PBE混合的奇怪之处,但如前所述,它是旧代码,用于解密/加密数据,现在我必须能够使用BC轻量级API读取加密数据。在这里我与SecretKeySpec斗争 - 在BC轻量级API中有类似的东西吗?
答案 0 :(得分:2)
更新问题的答案是,如果您为密码提供预先计算的AES密钥,则完全跳过完全。基本上,你只是在这里执行AES解密。如果您的keyBytes
确实是从密码中检索到的字节,那么您很可能容易受到与密码相关的攻击。</ p>
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
byte[] keyBytes = new byte[16];
byte[] iv = new byte[16];
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
javax.crypto.Cipher cipher = Cipher.getInstance("PBEWITHSHA256AND128BITAES-CBC-BC");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] ciphertext = cipher.doFinal("The PBE encryption has gone!".getBytes(UTF_8));
Cipher dCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
dCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
final byte[] plain = dCipher.doFinal(ciphertext);
System.out.println(new String(plain, UTF_8));
}
输出明文:
The PBE encryption has gone!
[添加了对alphakermit问题的直接回答]
要在Bouncy中执行解密,您需要执行基本的CBC解密,正如alphakermit在评论中所指出的那样:
KeyParameter keyParameter = new KeyParameter(aesDecrypted);
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(new AESEngine());
cbcBlockCipher.init(false, keyParameter);
PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(cbcBlockCipher, new PKCS7Padding());
return new CipherInputStream(in, aesCipher)
使用org.bouncycastle.crypto.io.CipherInputStream
而不是Java本身提供的那个。