From 61dac86bceb9d727e1bd707982c41ab9467c6d5a Mon Sep 17 00:00:00 2001
From: Maxim Thomas <maxim.thomas@gmail.com>
Date: Mon, 03 Nov 2025 06:30:05 +0000
Subject: [PATCH] Switch from sun.security.x509 to Bouncy Castle API (#560)
---
opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java | 285 ++++++++++++++++----------------------------------------
1 files changed, 83 insertions(+), 202 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java b/opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java
index 543f5f9..9289f99 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/util/Platform.java
@@ -13,30 +13,50 @@
*
* Copyright 2009-2010 Sun Microsystems, Inc.
* Portions Copyright 2013-2016 ForgeRock AS.
+ * Portions Copyright 2025 Wren Security.
+ * Portions Copyright 2025 3A Systems LLC.
*/
package org.opends.server.util;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import com.forgerock.opendj.util.StaticUtils;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.util.BigIntegers;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.util.Reject;
-import static org.opends.messages.UtilityMessages.*;
-import static org.opends.server.util.ServerConstants.CERTANDKEYGEN_PROVIDER;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_ADD_CERT;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_ALIAS_ALREADY_EXISTS;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_ALIAS_INVALID;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_CERT_REPLIES_INVALID;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_DELETE_ALIAS;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_GEN_SELF_SIGNED_CERT;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_KEYSTORE_NONEXISTANT;
+import static org.opends.messages.UtilityMessages.ERR_CERTMGR_TRUSTED_CERT;
/**
* Provides a wrapper class that collects all of the JVM vendor and JDK version
@@ -45,21 +65,6 @@
public final class Platform
{
- /** Prefix that determines which security package to use. */
- private static final String pkgPrefix;
-
- /** The two security package prefixes (IBM and SUN). */
- private static final String IBM_SEC = "com.ibm.security";
- private static final String SUN_SEC = "sun.security";
-
- /** The CertAndKeyGen class is located in different packages depending on JVM environment. */
- private static final String[] CERTANDKEYGEN_PATHS = new String[] {
- "sun.security.x509.CertAndKeyGen", // Oracle/Sun/OpenJDK 6,7
- "sun.security.tools.keytool.CertAndKeyGen", // Oracle/Sun/OpenJDK 8
- "com.ibm.security.x509.CertAndKeyGen", // IBM SDK 7
- "com.ibm.security.tools.CertAndKeyGen" // IBM SDK 8
- };
-
private static final PlatformIMPL IMPL;
/** The minimum java supported version. */
@@ -67,16 +72,6 @@
static
{
- String vendor = System.getProperty("java.vendor");
-
- if (vendor.startsWith("IBM"))
- {
- pkgPrefix = IBM_SEC;
- }
- else
- {
- pkgPrefix = SUN_SEC;
- }
IMPL = new DefaultPlatformIMPL();
}
@@ -84,10 +79,10 @@
public static enum KeyType
{
/** RSA key algorithm with 2048 bits size and SHA256withRSA signing algorithm. */
- RSA("rsa", 2048, "SHA256WithRSA"),
+ RSA("RSA", 2048, "SHA256withRSA"),
- /** Elliptic Curve key algorithm with 233 bits size and SHA256withECDSA signing algorithm. */
- EC("ec", 256, "SHA256withECDSA");
+ /** Elliptic Curve key algorithm with 256 bits size and SHA256withECDSA signing algorithm. */
+ EC("EC", 256, "SHA256withECDSA");
/** Default key type used when none can be determined. */
public final static KeyType DEFAULT = RSA;
@@ -104,22 +99,6 @@
}
/**
- * Check whether this key type is supported by the current JVM.
- * @return true if this key type is supported, false otherwise.
- */
- public boolean isSupported()
- {
- try
- {
- return KeyPairGenerator.getInstance(keyAlgorithm.toUpperCase()) != null;
- }
- catch (NoSuchAlgorithmException e)
- {
- return false;
- }
- }
-
- /**
* Get a KeyType based on the alias name.
*
* @param alias
@@ -144,106 +123,13 @@
*/
private static abstract class PlatformIMPL
{
- /** Time values used in validity calculations. */
- private static final int SEC_IN_DAY = 24 * 60 * 60;
-
- /** Methods pulled from the classes. */
- private static final String GENERATE_METHOD = "generate";
- private static final String GET_PRIVATE_KEY_METHOD = "getPrivateKey";
- private static final String GET_SELFSIGNED_CERT_METHOD =
- "getSelfCertificate";
-
- /** Classes needed to manage certificates. */
- private static final Class<?> certKeyGenClass, X500NameClass;
-
- /** Constructors for each of the above classes. */
- private static Constructor<?> certKeyGenCons, X500NameCons;
-
- /** Filesystem APIs */
-
- static
- {
-
- String certAndKeyGen = getCertAndKeyGenClassName();
- if(certAndKeyGen == null)
- {
- LocalizableMessage msg = ERR_CERTMGR_CERTGEN_NOT_FOUND.get(CERTANDKEYGEN_PROVIDER);
- throw new ExceptionInInitializerError(msg.toString());
- }
-
- String X500Name = pkgPrefix + ".x509.X500Name";
- try
- {
- certKeyGenClass = Class.forName(certAndKeyGen);
- X500NameClass = Class.forName(X500Name);
- certKeyGenCons = certKeyGenClass.getConstructor(String.class,
- String.class);
- X500NameCons = X500NameClass.getConstructor(String.class);
- }
- catch (ClassNotFoundException e)
- {
- LocalizableMessage msg = ERR_CERTMGR_CLASS_NOT_FOUND.get(e.getMessage());
- throw new ExceptionInInitializerError(msg.toString());
- }
- catch (SecurityException e)
- {
- LocalizableMessage msg = ERR_CERTMGR_SECURITY.get(e.getMessage());
- throw new ExceptionInInitializerError(msg.toString());
- }
- catch (NoSuchMethodException e)
- {
- LocalizableMessage msg = ERR_CERTMGR_NO_METHOD.get(e.getMessage());
- throw new ExceptionInInitializerError(msg.toString());
- }
- }
-
- /**
- * Try to decide which CertAndKeyGen class to use.
- *
- * @return a fully qualified class name or null
- */
- private static String getCertAndKeyGenClassName() {
- String certAndKeyGen = System.getProperty(CERTANDKEYGEN_PROVIDER);
- if (certAndKeyGen != null)
- {
- return certAndKeyGen;
- }
-
- for (String className : CERTANDKEYGEN_PATHS)
- {
- if (classExists(className))
- {
- return className;
- }
- }
- return null;
- }
-
- /**
- * A quick check to see if a class can be loaded. Doesn't check if
- * it can be instantiated.
- *
- * @param className full class name to check
- * @return true if the class is found
- */
- private static boolean classExists(final String className)
- {
- try {
- Class.forName(className);
- return true;
- } catch (ClassNotFoundException | ClassCastException e) {
- return false;
- }
- }
protected PlatformIMPL()
{
}
-
-
private final void deleteAlias(KeyStore ks, String ksPath, String alias,
- char[] pwd) throws KeyStoreException
+ char[] pwd) throws KeyStoreException
{
try
{
@@ -264,10 +150,8 @@
}
}
-
-
private final void addCertificate(KeyStore ks, String ksType, String ksPath,
- String alias, char[] pwd, String certPath) throws KeyStoreException
+ String alias, char[] pwd, String certPath) throws KeyStoreException
{
try
{
@@ -284,7 +168,7 @@
throw new KeyStoreException(msg.toString());
}
else if (!ks.containsAlias(alias)
- || ks.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class))
+ || ks.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class))
{
try (InputStream inStream = new FileInputStream(certPath)) {
trustedCert(alias, cf, ks, inStream);
@@ -305,14 +189,17 @@
}
}
-
-
private static final KeyStore generateSelfSignedCertificate(KeyStore ks,
- String ksType, String ksPath, KeyType keyType, String alias, char[] pwd, String dn,
- int validity) throws KeyStoreException
+ String ksType, String ksPath, KeyType keyType, String alias, char[] pwd, String dn,
+ int validity) throws KeyStoreException
{
+ boolean isFips = StaticUtils.isFips();
try
{
+ if(!isFips)
+ {
+ Security.addProvider(new BouncyCastleFipsProvider());
+ }
if (ks == null)
{
ks = KeyStore.getInstance(ksType);
@@ -324,12 +211,11 @@
throw new KeyStoreException(msg.toString());
}
- final Object keypair = newKeyPair(keyType);
- final Object subject = newX500Name(dn);
- generate(keypair, keyType.keySize);
- final PrivateKey privateKey = getPrivateKey(keypair);
- final Certificate[] certificateChain = new Certificate[] {
- getSelfCertificate(keypair, subject, validity * SEC_IN_DAY)
+ KeyPair keyPair = newKeyPair(keyType);
+ PrivateKey privateKey = keyPair.getPrivate();
+ X500Name subject = new X500Name(dn);
+ Certificate[] certificateChain = new Certificate[] {
+ generateSelfCertificate(keyPair, keyType, subject, validity)
};
ks.setKeyEntry(alias, privateKey, pwd, certificateChain);
try (FileOutputStream fileOutStream = new FileOutputStream(ksPath)) {
@@ -341,34 +227,41 @@
{
throw new KeyStoreException(ERR_CERTMGR_GEN_SELF_SIGNED_CERT.get(alias, e.getMessage()).toString(), e);
}
+ finally
+ {
+ if(!isFips)
+ {
+ Security.removeProvider(BouncyCastleFipsProvider.PROVIDER_NAME);
+ }
+ }
}
- private static Object newKeyPair(KeyType keyType) throws Exception
+ private static KeyPair newKeyPair(KeyType keyType) throws Exception
{
- return certKeyGenCons.newInstance(keyType.keyAlgorithm, keyType.signatureAlgorithm);
+ KeyPairGenerator generator = KeyPairGenerator.getInstance(keyType.keyAlgorithm, BouncyCastleFipsProvider.PROVIDER_NAME);
+ generator.initialize(keyType.keySize);
+ return generator.generateKeyPair();
}
- private static Object newX500Name(String dn) throws Exception
+ private static Certificate generateSelfCertificate(KeyPair keyPair, KeyType keyType, X500Name subject, int days) throws Exception
{
- return X500NameCons.newInstance(dn);
- }
+ BigInteger serial = BigIntegers.createRandomBigInteger(64, new SecureRandom());
+ Instant now = Instant.now();
+ Date notBeforeDate = Date.from(now);
+ Date notAfterDate = Date.from(now.plus(days, ChronoUnit.DAYS));
- private static void generate(Object keypair, int keySize) throws Exception
- {
- Method certAndKeyGenGenerate = certKeyGenClass.getMethod(GENERATE_METHOD, int.class);
- certAndKeyGenGenerate.invoke(keypair, keySize);
- }
+ JcaX509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
+ subject, serial, notBeforeDate, notAfterDate, subject, keyPair.getPublic()
+ );
+ ContentSigner signer = new JcaContentSignerBuilder(keyType.signatureAlgorithm)
+ .setProvider(BouncyCastleFipsProvider.PROVIDER_NAME)
+ .build(keyPair.getPrivate());
+ X509CertificateHolder holder = builder.build(signer);
+ JcaX509CertificateConverter converter = new JcaX509CertificateConverter()
+ .setProvider(BouncyCastleFipsProvider.PROVIDER_NAME);
- private static PrivateKey getPrivateKey(Object keypair) throws Exception
- {
- Method certAndKeyGetPrivateKey = certKeyGenClass.getMethod(GET_PRIVATE_KEY_METHOD);
- return (PrivateKey) certAndKeyGetPrivateKey.invoke(keypair);
- }
- private static Certificate getSelfCertificate(Object keypair, Object subject, int days) throws Exception
- {
- Method getSelfCertificate = certKeyGenClass.getMethod(GET_SELFSIGNED_CERT_METHOD, X500NameClass, long.class);
- return (Certificate) getSelfCertificate.invoke(keypair, subject, days);
+ return converter.getCertificate(holder);
}
/**
@@ -376,7 +269,7 @@
* only if it is self-signed.
*/
private void trustedCert(String alias, CertificateFactory cf, KeyStore ks,
- InputStream in) throws KeyStoreException
+ InputStream in) throws KeyStoreException
{
try
{
@@ -398,8 +291,6 @@
}
}
-
-
/**
* Check that the issuer and subject DNs match.
*/
@@ -409,15 +300,11 @@
}
}
-
-
/** Prevent instantiation. */
private Platform()
{
}
-
-
/**
* Add the certificate in the specified path to the provided keystore;
* creating the keystore with the provided type and path if it doesn't exist.
@@ -439,13 +326,11 @@
* If an error occurred adding the certificate to the keystore.
*/
public static void addCertificate(KeyStore ks, String ksType, String ksPath,
- String alias, char[] pwd, String certPath) throws KeyStoreException
+ String alias, char[] pwd, String certPath) throws KeyStoreException
{
IMPL.addCertificate(ks, ksType, ksPath, alias, pwd, certPath);
}
-
-
/**
* Delete the specified alias from the provided keystore.
*
@@ -461,13 +346,11 @@
* If an error occurred deleting the alias.
*/
public static void deleteAlias(KeyStore ks, String ksPath, String alias,
- char[] pwd) throws KeyStoreException
+ char[] pwd) throws KeyStoreException
{
IMPL.deleteAlias(ks, ksPath, alias, pwd);
}
-
-
/**
* Generate a self-signed certificate using the specified alias, dn string and
* validity period. If the keystore does not exist, it will be created using
@@ -494,8 +377,8 @@
* If the self-signed certificate cannot be generated.
*/
public static void generateSelfSignedCertificate(KeyStore ks, String ksType,
- String ksPath, KeyType keyType, String alias, char[] pwd, String dn, int validity)
- throws KeyStoreException
+ String ksPath, KeyType keyType, String alias, char[] pwd, String dn, int validity)
+ throws KeyStoreException
{
PlatformIMPL.generateSelfSignedCertificate(ks, ksType, ksPath, keyType, alias, pwd, dn, validity);
}
@@ -507,8 +390,6 @@
{
}
-
-
/**
* Test if a platform java vendor property starts with the specified vendor
* string.
@@ -539,4 +420,4 @@
Reject.ifTrue(cpuMultiplier < 0, "Multiplier must be a positive number");
return Math.max(minimumValue, (int)(Runtime.getRuntime().availableProcessors() * cpuMultiplier));
}
-}
+}
\ No newline at end of file
--
Gitblit v1.10.0