I have a block of ciphertext that was created using the JCE algorithim "PBEWithSHA256And256BitAES-CBC-BC". The provider is BouncyCastle. What I'd like to do it decrypt this ciphertext using the BouncyCastle lightweight API. I don't want to use JCE because that requires installing the Unlimited Strength Jurisdiction Policy Files.
Documentation seems to be thin on the ground when it comes to using BC with PBE and AES.
Here's what I have so far. The decryption code runs without exception but returns rubbish.
The encryption code,
String password = "qwerty";
String plainText = "hello world";
byte[] salt = generateSalt();
byte[] cipherText = encrypt(plainText, password.toCharArray(), salt);
private static byte[] generateSalt() throws NoSuchAlgorithmException {
byte salt[] = new byte[8];
SecureRandom saltGen = SecureRandom.getInstance("SHA1PRNG");
saltGen.nextBytes(salt);
return salt;
}
private static byte[] encrypt(String plainText, char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
Security.addProvider(new BouncyCastleProvider());
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 20);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher encryptionCipher = Cipher.getInstance("PBEWithSHA256And256BitAES-CBC-BC");
encryptionCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
return encryptionCipher.doFinal(plainText.getBytes());
}
The decryption code,
byte[] decryptedText = decrypt(cipherText, password.getBytes(), salt);
private static byte[] decrypt(byte[] cipherText, byte[] password, byte[] salt) throws DataLengthException, IllegalStateException, InvalidCipherTextException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
BlockCipher engine = new AESEngine();
CBCBlockCipher cipher = new CBCBlockCipher(engine);
PKCS5S1ParametersGenerator keyGenerator = new PKCS5S1ParametersGenerator(new SHA256Digest());
keyGenerator.init(password, salt, 20);
CipherParameters keyParams = keyGenerator.generateDerivedParameters(256);
cipher.init(false, keyParams);
byte[] decryptedBytes = new byte[cipherText.length];
int numBytesCopied = cipher.processBlock(cipherText, 0, decryptedBytes, 0);
return decryptedBytes;
}
There were a few problems with your decrypt method:
The main problems were the way you were carrying out the decryption without using a block cipher and the missing IV size to the
generateDerivedParameters
method. I saw the first problem pretty quickly, the 2nd one was much less obvious. I only discovered that one through looking at a Bouncy Castle test called PBETest.I tried this and it seemed to work. Borrowed heavily from the BC class
org.bouncycastle.jce.provider.test.PBETest
It's not trivial to generate the key exactly as the JCE counterparts. I just briefly browsed your code. Found at least one discrepancy. JCE uses PKCS12 generator but you use PKCS5S1.
I am not surprised if there are other differences. You need to compare your code against BC source.
I noticed that your encryption method accepts a password as a char array but the decrypt accepts password as bytes. In Java chars are 16-bit while bytes are 8-bit. This could result in different keys for encrypt/decrypt and perhaps explain the issues with gibberish decrypted results?