bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

BaseCipher ignores supplied IV when PBEParameterSpec used

Open pwinckles opened this issue 1 year ago • 1 comments

There appears to be a bug when attempting to use PBKDF2 with AES encryption in approved only mode, where the PBEWITH* Cipher/SecretKeyFactory algorithm family is unavailable.

Example code:

import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.SecureRandom;

var keyAlgo = "PBKDF2WITHHMACSHA512";
var cipherAlgo = "2.16.840.1.101.3.4.1.42";

var provider = new BouncyCastleFipsProvider();
var random = SecureRandom.getInstance("SHA1PRNG");

var password = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".toCharArray();
var pbeKeySpec = new PBEKeySpec(password);

var factory = SecretKeyFactory.getInstance(keyAlgo, provider);
var key = factory.generateSecret(pbeKeySpec);

var salt = new byte[16];
random.nextBytes(salt);
var iv = new byte[16];
random.nextBytes(iv);

var parameterSpec = new PBEParameterSpec(salt, 1000, new IvParameterSpec(iv));

var encryptCipher = Cipher.getInstance(cipherAlgo, provider);
var decryptCipher = Cipher.getInstance(cipherAlgo, provider);

encryptCipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
decryptCipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);

var encryptedBytes = encryptCipher.doFinal("testing".getBytes());
var decryptedBytes = decryptCipher.doFinal(encryptedBytes);
var decryptedStr = new String(decryptedBytes, "UTF-8");

System.out.println(decryptedStr);

The above produced the following exception on decryption:

javax.crypto.BadPaddingException: Error finalising cipher data: pad block corrupted

The reason for this is because in BaseCipher.doEngineInit() it's ignoring the IV specified in parameterSpec and generates a new, random IV.

The bug can be fixed easily enough by pulling the IvParamterSpec out of PBEParameterSpec with getParameterSpec() either in BaseCipher or in the ParametersCreator that BaseCipher calls.

pwinckles avatar Sep 20 '24 17:09 pwinckles

So, it's going to take a bit of work to add this, but probably a lot more time to certify it. I'll put it on the list, but in the meanwhile, you can also do what you want with:

      PBEKeySpec fullPbeKeySpec = new PBEKeySpec(password, salt, 1000, 256);
      SecretKey fullKey = factory.generateSecret(fullPbeKeySpec);

      encryptCipher = Cipher.getInstance(cipherAlgo, "BCFIPS");
      decryptCipher = Cipher.getInstance(cipherAlgo, "BCFIPS");

      encryptCipher.init(Cipher.ENCRYPT_MODE, fullKey, pbeParamSpec.getParameterSpec());
      decryptCipher.init(Cipher.DECRYPT_MODE, fullKey, new IvParameterSpec(iv));

      System.out.println(Arrays.areEqual(input, decryptedBytes));

PBE support in the JCE was a bit ill-defined in the early days, so the other advantage of the above is it should be fairly portable.

dghgit avatar Dec 26 '24 08:12 dghgit