diff --git a/src/main/java/org/cryptacular/util/CertUtil.java b/src/main/java/org/cryptacular/util/CertUtil.java index 9bef6ce..b4288af 100644 --- a/src/main/java/org/cryptacular/util/CertUtil.java +++ b/src/main/java/org/cryptacular/util/CertUtil.java @@ -10,6 +10,8 @@ import java.nio.CharBuffer; import java.security.KeyPair; import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; @@ -37,7 +39,6 @@ import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; @@ -596,7 +597,9 @@ public static X509Certificate generateX509Certificate( final BigInteger serial = BigInteger.valueOf(now.toEpochMilli()); try { - final ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgo).build(keyPair.getPrivate()); + final Provider provider = bouncyCastleProvider(); + final ContentSigner contentSigner = + new JcaContentSignerBuilder(signatureAlgo).setProvider(provider).build(keyPair.getPrivate()); final X500Name x500Name = new X500Name(RFC4519Style.INSTANCE, dn); final X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(x500Name, @@ -607,12 +610,30 @@ public static X509Certificate generateX509Certificate( keyPair.getPublic()) .addExtension(Extension.basicConstraints, true, new BasicConstraints(true)); return new JcaX509CertificateConverter() - .setProvider(new BouncyCastleProvider()).getCertificate(certificateBuilder.build(contentSigner)); + .setProvider(provider).getCertificate(certificateBuilder.build(contentSigner)); } catch (OperatorCreationException | CertIOException | CertificateException e) { throw new RuntimeException("Certificate generation error", e); } } + static Provider bouncyCastleProvider() + { + Provider p = Security.getProvider("BCFIPS"); + if (p != null) { + return p; + } + p = Security.getProvider("BC"); + if (p != null) { + return p; + } + try { + return (Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider") + .getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException("No BouncyCastle provider found (BC or BCFIPS)", e); + } + } + /** * Describes the behavior of string formatting of X.500 distinguished names. */ diff --git a/src/main/java/org/cryptacular/util/CsrUtil.java b/src/main/java/org/cryptacular/util/CsrUtil.java index a764be7..80cbc4a 100644 --- a/src/main/java/org/cryptacular/util/CsrUtil.java +++ b/src/main/java/org/cryptacular/util/CsrUtil.java @@ -271,7 +271,8 @@ public static PKCS10CertificationRequest generateCsr( throw new CryptoException("Error adding subject alt names to CSR", e); } } - final JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(sigAlg); + final JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder(sigAlg) + .setProvider(CertUtil.bouncyCastleProvider()); try { final ContentSigner signer = csBuilder.build(keyPair.getPrivate()); return p10Builder.build(signer); diff --git a/src/test/java/org/cryptacular/util/CertUtilTest.java b/src/test/java/org/cryptacular/util/CertUtilTest.java index 4cdbec0..204e161 100644 --- a/src/test/java/org/cryptacular/util/CertUtilTest.java +++ b/src/test/java/org/cryptacular/util/CertUtilTest.java @@ -5,7 +5,9 @@ import java.nio.file.Files; import java.security.KeyPair; import java.security.PrivateKey; +import java.security.Provider; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; @@ -15,6 +17,7 @@ import java.util.List; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.cryptacular.FailListener; import org.cryptacular.generator.KeyPairGenerator; import org.cryptacular.x509.GeneralNameType; @@ -498,6 +501,49 @@ public void testGenX509UnSupportedAlgo() } + @Test + public void testBouncyCastleProviderFallsBackToBC() + { + Security.removeProvider("BC"); + try { + final Provider provider = CertUtil.bouncyCastleProvider(); + assertThat(provider).isNotNull(); + assertThat(provider.getName()).isEqualTo("BC"); + } finally { + Security.removeProvider("BC"); + } + } + + @Test + public void testBouncyCastleProviderUsesRegisteredBC() + { + final Provider bc = new BouncyCastleProvider(); + Security.addProvider(bc); + try { + final Provider provider = CertUtil.bouncyCastleProvider(); + assertThat(provider).isSameAs(bc); + } finally { + Security.removeProvider("BC"); + } + } + + @Test + public void testGenX509WithRegisteredBCProvider() + { + final Provider bc = new BouncyCastleProvider(); + Security.addProvider(bc); + try { + final KeyPair keyPair = KeyPairGenerator.generateRSA(new SecureRandom(), 2048); + final String dn = "CN=test.example.org,DC=example,DC=org"; + final X509Certificate cert = CertUtil.generateX509Certificate( + keyPair, dn, Duration.ofDays(365), "SHA256WithRSA"); + assertThat(cert).isNotNull(); + assertThat(CertUtil.subjectCN(cert)).isEqualTo("test.example.org"); + } finally { + Security.removeProvider("BC"); + } + } + private OffsetDateTime truncateToSeconds(final Instant instant) { return instant.atOffset(ZoneOffset.UTC).withNano(0);