Skip to content

Bouncy Castle FIPS 2.1.0: Loading a PKCS12 throws NoSuchAlgorithmException unless BCFIPS is registered in the JVM #2085

@francescomari

Description

@francescomari

Problem

I'm working in a system where I'm unable to register BCFIPS as a JVM security provider, and I'm forced to use it dynamically by passing it to the Java Security factories. I tried to open a PKCS12 key store (truststore.zip) with Bouncy Castle FIPS 2.1.0. When specifying BCFIPS as the security provider to the KeyStore, loading the key store fails with an IOException. This code:

InputStream in = getClass().getResourceAsStream("/truststore.p12");
KeyStore inputKeyStore = KeyStore.getInstance("pkcs12", new BouncyCastleFipsProvider());
inputKeyStore.load(in, "password".toCharArray());

throws this exception:

org.bouncycastle.jcajce.provider.ProvIOException: exception decrypting data - java.security.NoSuchAlgorithmException: Cannot find any provider supporting 1.2.840.113549.3.7
        at org.bouncycastle.jcajce.provider.ProvPKCS12$PKCS12KeyStoreSpi.cryptData(Unknown Source)
        at org.bouncycastle.jcajce.provider.ProvPKCS12$PKCS12KeyStoreSpi.engineLoad(Unknown Source)
        at java.base/java.security.KeyStore.load(KeyStore.java:1500)
        at com.example.KeyStoreTest.testLoadWithExplicitProvider(KeyStoreTest.java:54)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.security.NoSuchAlgorithmException: Cannot find any provider supporting 1.2.840.113549.3.7
        at java.base/javax.crypto.Cipher.getInstance(Cipher.java:574)
        at org.bouncycastle.jcajce.provider.ProvPKCS12$PKCS12KeyStoreSpi.createPBES2Cipher(Unknown Source)
        ... 7 more

Analysis

BCFIPS already has a mapping for 1.2.840.113549.3.7, which is des-ede3-cbc. In fact, when installing BCFIPS as a security provider in the JVM, the code above does not fail.

Provider provider = new BouncyCastleFipsProvider();
InputStream in = getClass().getResourceAsStream("/truststore.p12");
KeyStore inputKeyStore = KeyStore.getInstance("pkcs12", provider);
Security.addProvider(provider);
inputKeyStore.load(in, "password".toCharArray());

Please note that my JVM (OpenJDK 21.0.7) is not able to understand that key store out of the box. In fact, this code:

InputStream in = getClass().getResourceAsStream("/truststore.p12");
KeyStore inputKeyStore = KeyStore.getInstance("pkcs12");
inputKeyStore.load(in, "password".toCharArray());

throws this exception:

java.io.IOException: PBE parameter parsing error: expecting the object identifier for AES cipher
        at java.base/com.sun.crypto.provider.PBES2Parameters.parseES(PBES2Parameters.java:324)
        at java.base/com.sun.crypto.provider.PBES2Parameters.engineInit(PBES2Parameters.java:240)
        at java.base/java.security.AlgorithmParameters.init(AlgorithmParameters.java:311)
        at java.base/sun.security.x509.AlgorithmId.decodeParams(AlgorithmId.java:149)
        at java.base/sun.security.x509.AlgorithmId.<init>(AlgorithmId.java:131)
        at java.base/sun.security.x509.AlgorithmId.parse(AlgorithmId.java:416)
        at java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2052)
        at java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:228)
        at java.base/java.security.KeyStore.load(KeyStore.java:1500)
        at com.example.KeyStoreTest.testLoadWithDefaultProvider(KeyStoreTest.java:70)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
        at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)

The only way to make the code from the example above work is to install BCFIPS as the highest priority security provider, so that its implementation is chosen both for the key store and for the cryptographic algorithm with OID 1.2.840.113549.3.7, as in the following code:

InputStream in = getClass().getResourceAsStream("/truststore.p12");
Security.insertProviderAt(new BouncyCastleFipsProvider(), 1);
KeyStore inputKeyStore = KeyStore.getInstance("pkcs12");
inputKeyStore.load(in, "password".toCharArray());

As stated at the beginning of this issue, this solution is unsatisfactory. I can't change the configuration of my JVM globally to insert a new security provider at runtime.

Possible solution

While I don't have access to the BCFIPS source code, I've noticed some inconsistencies from the decompiled version of ProvPKCS12. In one place, the Cipher for a specific entry is retrieved with:

Cipher cipher = Cipher.getInstance(algId.getAlgorithm().getId(), fipsProvider);

In another place in the same class, the Cipher is instead retrieved with:

Cipher cipher = Cipher.getInstance(encScheme.getAlgorithm().getId());

I suspect that retrieving the Cipher and consistently specifying the fipsProvider every time would make my problem disappear, but without access to the source code I can't create a debug build to prove my theory.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions