Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
/** This reproduction scenario by Marnix Klooster is public domain. */
package marnix.experiment.bcfips;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
* Reproduction scenario for unexplained approved/unapproved exception. Run with
* bc-fips-1.0.2.jar and bctls-fips-1.0.10.jar. Run on a standard Java 8 JRE (I
* used Corretto), without any lib/security modifications or lib/ext additions,
* no SecurityManager, and not setting the 'approved-only mode' JVM property.
* As recommended in the bc-fips-1.0.2 user guide for Java 9+, this attempts to
* run without the Oracle/OpenJDK SunJSSE, and instead use BCJSSE from
* bctsl-fips.
* However, that combination (1) seems to require manual trust manager creation,
* and (2) inexplicably fails with an error about mixing approved/unapproved
* modes.
* Specifically, it seems that a DigestOutputStream object is created in one
* mode, then stored statically somewhere, and then used in the other mode...
* This seems related to the discussion in this thread:
public class MainForBCFIPSMixedApprovedOnlyIssue {
public static void main(String[] args) throws Throwable {
// workaround for just this experiment, so that we don't have to have cacerts in
// BCFKS mode...
System.setProperty("org.bouncycastle.jca.enable_jks", "true");
Security.insertProviderAt(new BouncyCastleFipsProvider(), 1); // insert at front to make sure it's preferred
// The following workaround for an SNI restriction in BCJSSE is from
// and the bctls 1.0.10 user guide, section 3.5.1.
// There they offer a better solution, which seems a lot more involved.
System.setProperty("jdk.tls.trustNameService", "true");
Security.insertProviderAt(new BouncyCastleJsseProvider("fips:BCFIPS"), 2);
for (boolean approvedOnlyMode : new boolean[] { false, true }) { // order of 'false' and 'true' doesn't matter
Throwable threadException[] = new Throwable[1];
Thread thread = new Thread(() -> {
try {
assert approvedOnlyMode == CryptoServicesRegistrar.isInApprovedOnlyMode();
// It looks like this is needed, to make the below HTTPS connection work.
TrustManagerFactory tmf = TrustManagerFactory
tmf.init((KeyStore) null);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, null);
SSLContext.setDefault(context); // fails if SunJSSE.isFIPS() == true
// The following fails the second time, with
// Caused by: org.bouncycastle.crypto.fips.FipsUnapprovedOperationError: Attempt to use unapproved implementation in approved thread: SHA-512
// at Source)
// at Source)
// at org.bouncycastle.crypto.UpdateOutputStream.update(Unknown Source)
// at org.bouncycastle.jcajce.provider.BaseMessageDigest.engineUpdate(Unknown Source)
// at
// 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
// at org.bouncycastle.tls.crypto.impl.jcajce.JcaNonceGenerator.<init>(Unknown Source)
// at ...
// (or with "...approved implementation in unapproved thread...",
// if true, false is used above).
assert new URL("").openConnection().getInputStream().read(new byte[4096]) != -1;
} catch (Throwable e) {
threadException[0] = e;
}, "Thread-approvedOnlyMode-" + approvedOnlyMode);
if (threadException[0] != null) {
// to make this unit test fail
throw new RuntimeException("error in approvedOnlyMode==" + approvedOnlyMode + ": " + threadException[0],
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment