How to selectively use approved-only mode with BouncyCastle JSSE provider + FIPS provider?

Tags: , , ,



In a small piece of sample code running on Java 8, I’m trying to follow the advice from the bc-fips-1.0.2.jar user guide, Legion of the Bouncy Castle Inc. BC-FJA 1.0.2 (Bouncy Castle FIPS Java API) User Guide Date: 09/14/19:

Note: support for FIPS mode appears to have started disappearing with Java 1.9, it has been confirmed that it is a null operation as of Java 11. For Java 11, or later, we recommend using the BCJSSE provider if you need FIPS support.

(footnote on page 11). And I’m doing this in a ‘mixed’ context, so most threads run in default mode, and some run in FIPS approved-only mode.

However, that doesn’t seem to work: It seems an ‘approved’ thread is trying to reuse and instance created by an ‘unapproved’ thread.

Specifically, when programmatically enabling bc-fips-1.0.2.jar’s BCFIPS and bctls-fips-1.0.10.jar’s BCJSSE, on a standard unmodified OpenJDK 8 (Corretto 8u232 in my case), and programmatically removing the built-in SunJSSE provider, then doing an HTTPS connection on a new thread in unapproved mode, then on another new thread in approved mode, results in an exception:

Caused by: org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: Attempt to use unapproved implementation in approved thread: SHA-512
    at org.bouncycastle.crypto.internal.io.Utils.approvedModeCheck(Unknown Source)
    at org.bouncycastle.crypto.internal.io.DigestOutputStream.write(Unknown Source)
    at org.bouncycastle.crypto.UpdateOutputStream.update(Unknown Source)
    at org.bouncycastle.jcajce.provider.BaseMessageDigest.engineUpdate(Unknown Source)
    at java.security.MessageDigest.update(MessageDigest.java:335)
    at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.runDigest(Unknown Source)
    at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider$NonceEntropySource$NonceEntropySourceSpi.engineNextBytes(Unknown Source)
    at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
    at org.bouncycastle.tls.crypto.impl.jcajce.JcaNonceGenerator.<init>(Unknown Source)
    at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createNonceGenerator(Unknown Source)
    at org.bouncycastle.tls.AbstractTlsContext.createNonceGenerator(Unknown Source)
    at org.bouncycastle.tls.AbstractTlsContext.<init>(Unknown Source)
    at org.bouncycastle.tls.TlsClientContextImpl.<init>(Unknown Source)
[etc]

Full reproduction scenario of my attempts is at https://gist.github.com/marnix/834610d0fb92e53ce507edfce96bacb9, see the class’s javadoc for details.

So my question is: How do I make HTTPS connections work, where some are in the default mode and others are in FIPS approved-only mode, using BCJSSE instead of SunJSSE?

Or will this not work on Java 8, and do I have to wait for Java 9+ for this to start working? (Why?)

(My ultimate goal is not for HTTPS connections but for other things, where most will run in the default mode, and some threads will run in approved-only mode.)

Answer

Using separate SSLContext instances is the right approach, but using SSLContext.setDefault is very wrong here. Partly because once you remove the thread joins, there will be race conditions b/w the setters. More importantly though, HttpsURLConnection only sets its default SSLSocketFactory once, and will call SSLContext.getDefault at most once to set it, so calling SSLContext.setDefault after that will have no effect on HttpsURLConnection, and you will get sharing of the original SSLContext and FIPS errors.

Remove:

SSLContext.setDefault(context);

and replace:

url.openConnection()

with:

HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(context.getSocketFactory());


Source: stackoverflow