From 2cf46088b7e69b4f424a821291607afe6faa7e4f Mon Sep 17 00:00:00 2001
From: Yuriy Movchan <Yuriy.Movchan@gmail.com>
Date: Fri, 30 Jul 2021 14:08:39 +0000
Subject: [PATCH] Add FIPS support (#176)

---
 opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java                                                   |    3 
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml              |    4 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedTrustManagerProvider.java                               |   12 
 opendj-server-legacy/src/main/java/org/opends/server/util/StaticUtils.java                                                       |   15 +
 opendj-cli/src/main/java/com/forgerock/opendj/cli/ConnectionFactoryProvider.java                                                 |   60 ++++-
 opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java                                                                     |    3 
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/PKCS11TrustManagerProviderConfiguration.xml |   52 ++++
 opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java                                                |   69 ++++++
 opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ApplicationTrustManager.java                                        |    5 
 opendj-server-legacy/src/main/java/org/opends/server/extensions/PKCS11TrustManagerProvider.java                                  |  188 +++++++++++++++++
 opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java                                            |   11 
 opendj-core/src/main/java/com/forgerock/opendj/util/StaticUtils.java                                                             |   30 ++
 opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java                                                     |    3 
 opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ConnectionWrapper.java                                              |   14 +
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java                                             |   33 ++
 opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java                                                   |    3 
 opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java                                              |   11 
 opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/SetupLauncher.java                                            |    4 
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java                                                  |   11 +
 opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties                                                         |    2 
 opendj-server-legacy/src/messages/org/opends/messages/extension.properties                                                       |    3 
 opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java                                                      |   52 ++++
 opendj-server-legacy/src/main/java/org/opends/server/tools/InstallDS.java                                                        |   18 +
 opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java                                                           |   17 +
 opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java                                     |   10 
 opendj-core/pom.xml                                                                                                              |   16 +
 26 files changed, 616 insertions(+), 33 deletions(-)

diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConnectionFactoryProvider.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConnectionFactoryProvider.java
index 3c70009..f9e3344 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConnectionFactoryProvider.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/ConnectionFactoryProvider.java
@@ -34,12 +34,15 @@
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.X509KeyManager;
@@ -63,6 +66,8 @@
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.util.Options;
 
+import com.forgerock.opendj.util.StaticUtils;
+
 /** A connection factory designed for use with command line tools. */
 public final class ConnectionFactoryProvider {
     /** The Logger. */
@@ -356,7 +361,10 @@
      *         If an SSL context could not be created.
      */
     public static List<String> getDefaultProtocols() throws NoSuchAlgorithmException {
-        List<String> enabled = Arrays.asList(SSLContext.getDefault().createSSLEngine().getEnabledProtocols());
+    	return getDefaultProtocols(SSLContext.getDefault());
+    }
+    public static List<String> getDefaultProtocols(SSLContext sslContext) throws NoSuchAlgorithmException {
+        List<String> enabled = Arrays.asList(sslContext.createSSLEngine().getEnabledProtocols());
         final String property = System.getProperty("org.opends.ldaps.protocols");
         final List<String> defaults = new ArrayList<>();
         if (property != null && property.length() != 0) {
@@ -445,6 +453,8 @@
 
                         if (akm != null && clientAlias != null) {
                             keyManager = KeyManagers.useSingleCertificate(clientAlias, akm);
+                        } else {
+                        	keyManager = akm;
                         }
 
                         sslContext =
@@ -462,7 +472,7 @@
                 try {
                     options.set(SSL_CONTEXT, sslContext)
                             .set(SSL_USE_STARTTLS, useStartTLSArg.isPresent())
-                            .set(SSL_ENABLED_PROTOCOLS, getDefaultProtocols());
+                            .set(SSL_ENABLED_PROTOCOLS, getDefaultProtocols(sslContext));
                 } catch (NoSuchAlgorithmException e) {
                     throw new ArgumentException(ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL.get(e.toString()), e);
                 }
@@ -694,9 +704,10 @@
      *             If a problem occurs while loading with the key store.
      * @throws CertificateException
      *             If a problem occurs while loading with the key store.
+     * @throws UnrecoverableKeyException 
      */
     public X509KeyManager getKeyManager(String keyStoreFile) throws KeyStoreException,
-            IOException, NoSuchAlgorithmException, CertificateException {
+            IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
         if (keyStoreFile == null) {
             // Lookup the file name through the JDK property.
             keyStoreFile = getKeyStore();
@@ -712,11 +723,29 @@
             keyStorePIN = keyStorePass.toCharArray();
         }
 
-        final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
-        try (final FileInputStream fos = new FileInputStream(keyStoreFile)) {
-            keystore.load(fos, keyStorePIN);
+        boolean isFips = StaticUtils.isFips();
+        final String keyStoreType = KeyStore.getDefaultType();
+        final KeyStore keystore = KeyStore.getInstance(keyStoreType);
+        if (isFips) {
+            keystore.load(null, keyStorePIN);
+        } else {
+	        try (final FileInputStream fos = new FileInputStream(keyStoreFile)) {
+	            keystore.load(fos, keyStorePIN);
+	        }
         }
 
+        if (isFips) {
+	        String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+	        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(keyManagerAlgorithm);
+	        keyManagerFactory.init(keystore, keyStorePIN);
+	        
+	        for (final KeyManager km : keyManagerFactory.getKeyManagers()) {
+	            if (km instanceof X509KeyManager) {
+	                return (X509KeyManager) km;
+	            }
+	        }
+        }
+        
         return new ApplicationKeyManager(keystore, keyStorePIN);
     }
 
@@ -802,16 +831,25 @@
             return TrustManagers.trustAll();
         }
 
+        boolean isFips = StaticUtils.isFips();
         X509TrustManager tm = null;
         if (trustStorePathArg.isPresent() && trustStorePathArg.getValue().length() > 0) {
-            tm = TrustManagers.checkValidityDates(TrustManagers.checkHostName(hostNameArg.getValue(),
-                    TrustManagers.checkUsingTrustStore(trustStorePathArg.getValue(), getTrustStorePIN(), null)));
+        	if (isFips) {
+	            tm = TrustManagers.checkUsingTrustStore(trustStorePathArg.getValue(), getTrustStorePIN(), null);
+        	} else {
+	            tm = TrustManagers.checkValidityDates(TrustManagers.checkHostName(hostNameArg.getValue(),
+	                    TrustManagers.checkUsingTrustStore(trustStorePathArg.getValue(), getTrustStorePIN(), null)));
+        	}
         } else if (getTrustStore() != null) {
-            tm = TrustManagers.checkValidityDates(TrustManagers.checkHostName(hostNameArg.getValue(),
-                    TrustManagers.checkUsingTrustStore(getTrustStore(), getTrustStorePIN(), null)));
+        	if (isFips) {
+	            tm = TrustManagers.checkUsingTrustStore(getTrustStore(), getTrustStorePIN(), null);
+        	} else {
+                tm = TrustManagers.checkValidityDates(TrustManagers.checkHostName(hostNameArg.getValue(),
+                        TrustManagers.checkUsingTrustStore(getTrustStore(), getTrustStorePIN(), null)));
+        	}
         }
 
-        if (app != null && !app.isQuiet()) {
+        if (app != null && !app.isQuiet() && !isFips) {
             return new PromptingTrustManager(app, tm);
         }
 
diff --git a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
index a4d48f0..0e3658f 100644
--- a/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
+++ b/opendj-cli/src/main/java/com/forgerock/opendj/cli/Utils.java
@@ -29,6 +29,8 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.security.GeneralSecurityException;
+import java.security.Provider;
+import java.security.Security;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Collection;
@@ -729,4 +731,5 @@
     public static LocalizableMessage conflictingArgsErrorMessage(final Argument arg1, final Argument arg2) {
         return ERR_TOOL_CONFLICTING_ARGS.get(arg1.getLongIdentifier(), arg2.getLongIdentifier());
     }
+
 }
diff --git a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
index fcba0db..44abe97 100644
--- a/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
+++ b/opendj-config/src/main/java/org/forgerock/opendj/config/dsconfig/DSConfig.java
@@ -28,6 +28,8 @@
 import static org.forgerock.opendj.config.PropertyOption.*;
 import static org.forgerock.opendj.config.dsconfig.ArgumentExceptionFactory.*;
 
+import static com.forgerock.opendj.util.StaticUtils.registerBcProvider;
+
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
@@ -1053,6 +1055,7 @@
     private int run(String[] args) {
         // Register global arguments and sub-commands.
         try {
+            registerBcProvider();
             initializeGlobalArguments();
             initializeSubCommands();
         } catch (ArgumentException e) {
diff --git a/opendj-core/pom.xml b/opendj-core/pom.xml
index 7413f11..a40b4db 100644
--- a/opendj-core/pom.xml
+++ b/opendj-core/pom.xml
@@ -90,10 +90,26 @@
 		  <groupId>com.sun.xml.bind</groupId>
 		  <artifactId>jaxb-impl</artifactId>
 		</dependency>
+
+	    <!-- BC FIPS Provider libs -->
+	    <dependency>
+	      <groupId>org.bouncycastle</groupId>
+	      <artifactId>bc-fips</artifactId>
+	      <version>${bc.fips.version}</version>
+	    </dependency>
+	
+	    <dependency>
+	      <groupId>org.bouncycastle</groupId>
+	      <artifactId>bctls-fips</artifactId>
+	      <version>${bctls.fips.version}</version>
+	    </dependency>
     </dependencies>
 
 
     <properties>
+        <bc.fips.version>1.0.2.1</bc.fips.version>
+        <bctls.fips.version>1.0.9</bctls.fips.version>
+
         <opendj.osgi.import.additional>
             com.sun.security.auth*;resolution:=optional
         </opendj.osgi.import.additional>
diff --git a/opendj-core/src/main/java/com/forgerock/opendj/util/StaticUtils.java b/opendj-core/src/main/java/com/forgerock/opendj/util/StaticUtils.java
index b60cb2a..2a48a1a 100644
--- a/opendj-core/src/main/java/com/forgerock/opendj/util/StaticUtils.java
+++ b/opendj-core/src/main/java/com/forgerock/opendj/util/StaticUtils.java
@@ -42,6 +42,9 @@
 import org.forgerock.util.Reject;
 import org.forgerock.util.Utils;
 
+import static com.forgerock.opendj.ldap.CoreMessages.INFO_BC_PROVIDER_REGISTER;
+import static com.forgerock.opendj.ldap.CoreMessages.INFO_BC_PROVIDER_REGISTERED_ALREADY;
+
 /**
  * Common utility methods.
  */
@@ -786,4 +789,31 @@
         }
     }
 
+    public static boolean isFips() {
+    	java.security.Provider[] providers = java.security.Security.getProviders();
+		for (int i = 0; i < providers.length; i++) {
+			if (providers[i].getName().toLowerCase().contains("fips"))
+				return true;
+		}
+
+		return false;
+	}
+
+    public static void registerBcProvider()
+    {
+    	if (!isFips()) {
+    		return;
+    	}
+    	
+    	org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider bouncyCastleProvider = (org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider) java.security.Security.getProvider(org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider.PROVIDER_NAME);
+  		if (bouncyCastleProvider == null) {
+  			logger.info(INFO_BC_PROVIDER_REGISTER.get());
+
+  			bouncyCastleProvider = new org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider();
+  			java.security.Security.insertProviderAt(bouncyCastleProvider, 1);
+  		} else {
+  			logger.info(INFO_BC_PROVIDER_REGISTERED_ALREADY.get());
+  		}
+    }
+
 }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
index ad3335c..42cc7d0 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
@@ -24,6 +24,8 @@
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
@@ -498,7 +500,10 @@
         Reject.ifNull(file);
 
         final File trustStoreFile = new File(file);
-        final String trustStoreFormat = format != null ? format : KeyStore.getDefaultType();
+
+        boolean isFips = isFips();
+        final String defaultType = isFips ? "JKS" : KeyStore.getDefaultType();
+        final String trustStoreFormat = format != null ? format : defaultType;
 
         final KeyStore keyStore = KeyStore.getInstance(trustStoreFormat);
         try (FileInputStream fos = new FileInputStream(trustStoreFile)) {
@@ -517,6 +522,16 @@
         throw new NoSuchAlgorithmException();
     }
 
+    public static boolean isFips() {
+		Provider[] providers = Security.getProviders();
+		for (int i = 0; i < providers.length; i++) {
+			if (providers[i].getName().toLowerCase().contains("fips"))
+				return true;
+		}
+
+		return false;
+	}
+
     /**
      * Wraps the provided {@code X509TrustManager} by adding additional
      * validation which rejects certificate chains containing certificates which
diff --git a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties b/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
index f5ec2c8..fd4eb60 100644
--- a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
+++ b/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
@@ -616,6 +616,8 @@
 INFO_RESULT_OFFSET_RANGE_ERROR=Offset Range Error
 INFO_RESULT_VIRTUAL_LIST_VIEW_ERROR=Virtual List View Error
 INFO_RESULT_NO_OPERATION=No Operation
+INFO_BC_PROVIDER_REGISTER=Registering Bouncy Castle Provider
+INFO_BC_PROVIDER_REGISTERED_ALREADY=Bouncy Castle Provider was registered already
 #
 # Protocol messages
 #
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
index bd638bb..8e7fb69 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
@@ -87,6 +87,8 @@
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
 import org.glassfish.grizzly.ssl.SSLFilter;
 
+import com.forgerock.opendj.util.StaticUtils;
+
 /** LDAP connection implementation. */
 final class GrizzlyLDAPConnection implements LDAPConnectionImpl, TimeoutEventListener {
     static final int LDAP_V3 = 3;
@@ -95,12 +97,15 @@
      * config. This prevents Grizzly from needlessly using JVM defaults which
      * may be incorrectly configured.
      */
-    private static final SSLEngineConfigurator DUMMY_SSL_ENGINE_CONFIGURATOR;
+    private static SSLEngineConfigurator DUMMY_SSL_ENGINE_CONFIGURATOR = null;
     static {
         try {
-            DUMMY_SSL_ENGINE_CONFIGURATOR =
-                    new SSLEngineConfigurator(new SSLContextBuilder().setTrustManager(
-                            TrustManagers.distrustAll()).getSSLContext());
+        	// We need to use FIPS compatible Trust Manasger in FIPS mode
+        	if (!StaticUtils.isFips()) {
+	        	DUMMY_SSL_ENGINE_CONFIGURATOR =
+	                    new SSLEngineConfigurator(new SSLContextBuilder().setTrustManager(
+	                            TrustManagers.distrustAll()).getSSLContext());
+        	}
         } catch (GeneralSecurityException e) {
             // This should never happen.
             throw new IllegalStateException("Unable to create Dummy SSL Engine Configurator", e);
@@ -823,14 +828,30 @@
             sslEngineConfigurator.setEnabledCipherSuites(cipherSuites.isEmpty() ? null : cipherSuites
                     .toArray(new String[cipherSuites.size()]));
             sslEngineConfigurator.setCipherConfigured(true);
-            final SSLFilter sslFilter = new SSLFilter(DUMMY_SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
+            
+            SSLEngineConfigurator serverSslEngineConfigurator = buildServerSSLEngineConfigurator(sslContext);
+            final SSLFilter sslFilter = new SSLFilter(serverSslEngineConfigurator, sslEngineConfigurator);
             sslFilter.setHandshakeTimeout(getLongProperty("org.forgerock.opendj.grizzly.handshakeTimeout", sslFilter.getHandshakeTimeout(TimeUnit.MILLISECONDS)), TimeUnit.MILLISECONDS);
             installFilter(sslFilter);
             sslFilter.handshake(connection, completionHandler);
         }
     }
 
-    private LdapException adaptRequestIOException(final IOException e) {
+    private SSLEngineConfigurator buildServerSSLEngineConfigurator(SSLContext sslContext) {
+        if (DUMMY_SSL_ENGINE_CONFIGURATOR != null) {
+        	return DUMMY_SSL_ENGINE_CONFIGURATOR;
+        }
+        
+        if (sslContext == null) {
+            throw new IllegalStateException("SSL context should be defined in FIPS mode");
+        }
+        
+        SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(sslContext);
+        
+        return sslEngineConfigurator;
+	}
+
+	private LdapException adaptRequestIOException(final IOException e) {
         // FIXME: what other sort of IOExceptions can be thrown?
         // FIXME: Is this the best result code?
         final Result errorResult = Responses.newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
index e2bf6a7..a50834d 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Properties;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -71,6 +72,7 @@
 import org.glassfish.grizzly.filterchain.FilterChainBuilder;
 import org.glassfish.grizzly.filterchain.FilterChainContext;
 import org.glassfish.grizzly.filterchain.NextAction;
+import org.glassfish.grizzly.ssl.SSLContextConfigurator;
 import org.glassfish.grizzly.ssl.SSLFilter;
 import org.glassfish.grizzly.ssl.SSLUtils;
 import org.reactivestreams.Publisher;
@@ -386,6 +388,15 @@
                     return false;
                 }
                 SSLUtils.setSSLEngine(connection, sslEngine);
+                
+                Properties props = System.getProperties();
+
+                // Workaround for PKCS11
+                String keyStoreFile = props.getProperty(SSLContextConfigurator.KEY_STORE_FILE);
+                if ("none".equalsIgnoreCase(keyStoreFile)) {
+                	System.setProperty(SSLContextConfigurator.TRUST_STORE_FILE, "NONE");
+                }
+                
                 SSLFilter sslFilter = new SSLFilter();
                 sslFilter.setHandshakeTimeout(getLongProperty("org.forgerock.opendj.grizzly.handshakeTimeout", sslFilter.getHandshakeTimeout(TimeUnit.MILLISECONDS)), TimeUnit.MILLISECONDS);
                 installFilter(startTls ? new StartTLSFilter(sslFilter) : sslFilter);
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
index d0eb549..54651f3 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/Utils.java
@@ -26,6 +26,8 @@
 import static com.forgerock.opendj.ldap.tools.LDAPToolException.newToolParamException;
 import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
 
+import static com.forgerock.opendj.util.StaticUtils.registerBcProvider;
+
 import java.io.BufferedReader;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -463,6 +465,7 @@
     @VisibleForTesting
     static int runTool(final ToolConsoleApplication tool, final String... args) {
         try {
+        	registerBcProvider();
             return tool.run(args);
         } catch (final LDAPToolException e) {
             e.printErrorMessage(tool);
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
index 9629cb6..5a93a89 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/CryptoManagerConfiguration.xml
@@ -48,7 +48,7 @@
     </adm:requires-admin-action>
     <adm:default-behavior>
       <adm:defined>
-        <adm:value>SHA-1</adm:value>
+        <adm:value>SHA-256</adm:value>
       </adm:defined>
     </adm:default-behavior>
     <adm:syntax>
@@ -75,7 +75,7 @@
     </adm:requires-admin-action>
     <adm:default-behavior>
       <adm:defined>
-        <adm:value>HmacSHA1</adm:value>
+        <adm:value>HmacSHA256</adm:value>
       </adm:defined>
     </adm:default-behavior>
     <adm:syntax>
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/PKCS11TrustManagerProviderConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/PKCS11TrustManagerProviderConfiguration.xml
new file mode 100644
index 0000000..d21bc63
--- /dev/null
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/PKCS11TrustManagerProviderConfiguration.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  The contents of this file are subject to the terms of the Common Development and
+  Distribution License (the License). You may not use this file except in compliance with the
+  License.
+
+  You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+  specific language governing permission and limitations under the License.
+
+  When distributing Covered Software, include this CDDL Header Notice in each file and include
+  the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+  Header, with the fields enclosed by brackets [] replaced by your own identifying
+  information: "Portions Copyright [year] [name of copyright owner]".
+
+  Copyright 2007-2008 Sun Microsystems, Inc.
+  Portions Copyright 2011 ForgeRock AS.
+  ! -->
+<adm:managed-object name="pkcs11-trust-manager-provider"
+  plural-name="pkcs11-trust-manager-providers"
+  package="org.forgerock.opendj.server.config" extends="trust-manager-provider"
+  xmlns:adm="http://opendj.forgerock.org/admin"
+  xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
+  <adm:synopsis>
+    The
+    <adm:user-friendly-name />
+    enables the server to access the private
+    key information through the PKCS11 interface.
+  </adm:synopsis>
+  <adm:description>
+    This standard interface is used by cryptographic accelerators and
+    hardware security modules.
+  </adm:description>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:name>ds-cfg-pkcs11-trust-manager-provider</ldap:name>
+      <ldap:superior>ds-cfg-trust-manager-provider</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:property-override name="java-class" advanced="true">
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          org.opends.server.extensions.PKCS11TrustManagerProvider
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+  </adm:property-override>
+  <adm:property-reference name="trust-store-pin" />
+  <adm:property-reference name="trust-store-pin-property" />
+  <adm:property-reference name="trust-store-pin-environment-variable" />
+  <adm:property-reference name="trust-store-pin-file" />
+</adm:managed-object>
diff --git a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
index 3ef1bf9..6746e51 100644
--- a/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
+++ b/opendj-server-legacy/src/main/java/org/forgerock/opendj/reactive/LDAPConnectionHandler2.java
@@ -22,6 +22,8 @@
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
+import static com.forgerock.opendj.util.StaticUtils.isFips;
+
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -92,6 +94,8 @@
 
 import com.forgerock.reactive.ReactiveHandler;
 import com.forgerock.reactive.Stream;
+import java.security.Provider;
+import java.security.Security;
 
 /**
  * This class defines a connection handler that will be used for communicating with clients over LDAP. It is actually
@@ -939,7 +943,11 @@
             final TrustManager[] trustManagers =
                     trustMgrDN == null ? null : serverContext.getTrustManagerProvider(trustMgrDN).getTrustManagers();
             SSLContext sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
-            sslContext.init(keyManagers, trustManagers, null);
+            if (isFips()) {
+            	sslContext.init(keyManagerProvider.getKeyManagers(), trustManagers, null);
+            } else {
+            	sslContext.init(keyManagers, trustManagers, null);
+            }
             return sslContext;
         } catch (Exception e) {
             logger.traceException(e);
diff --git a/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ApplicationTrustManager.java b/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ApplicationTrustManager.java
index 4d66575..bebb3d3 100644
--- a/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ApplicationTrustManager.java
+++ b/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ApplicationTrustManager.java
@@ -458,4 +458,9 @@
     }
     return hostMatch;
   }
+
+  public X509TrustManager getX509TrustManager() {
+	return trustManager;
+  }
+  
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ConnectionWrapper.java b/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ConnectionWrapper.java
index 12a1de1..341b9fc 100644
--- a/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ConnectionWrapper.java
+++ b/opendj-server-legacy/src/main/java/org/opends/admin/ads/util/ConnectionWrapper.java
@@ -26,6 +26,7 @@
 import java.io.Closeable;
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import javax.net.ssl.KeyManager;
@@ -152,9 +153,18 @@
     if (isLdaps || isStartTls)
     {
       try {
-        options.set(SSL_CONTEXT, getSSLContext(trustManager, keyManager))
+    	SSLContext sslContext = getSSLContext(trustManager, keyManager);
+
+    	List<String> defaultProtocols;
+    	if (trustManager == null) {
+    		defaultProtocols = ConnectionFactoryProvider.getDefaultProtocols();
+    	} else {
+    		defaultProtocols = ConnectionFactoryProvider.getDefaultProtocols(sslContext);
+    	}
+
+    	options.set(SSL_CONTEXT, sslContext)
                 .set(SSL_USE_STARTTLS, isStartTls)
-                .set(SSL_ENABLED_PROTOCOLS, ConnectionFactoryProvider.getDefaultProtocols());
+                .set(SSL_ENABLED_PROTOCOLS, defaultProtocols);
       } catch (NoSuchAlgorithmException e) {
           throw newLdapException(CLIENT_SIDE_PARAM_ERROR, "Unable to perform SSL initialization:" + e.getMessage());
       }
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
index b2da78b..5c36610 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/Installer.java
@@ -27,6 +27,7 @@
 import static org.opends.admin.ads.ServerDescriptor.ServerProperty.*;
 import static org.opends.admin.ads.util.ConnectionUtils.*;
 import static org.opends.admin.ads.util.PreferredConnection.Type.*;
+import static org.opends.messages.AdminMessages.WARN_ADMIN_SET_PERMISSIONS_FAILED;
 import static org.opends.messages.QuickSetupMessages.*;
 import static org.opends.quicksetup.Step.*;
 import static org.opends.quicksetup.installer.DataReplicationOptions.Type.*;
@@ -37,10 +38,15 @@
 import java.awt.event.WindowEvent;
 import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.security.KeyStore;
 import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -53,6 +59,8 @@
 import java.util.Set;
 
 import javax.naming.ldap.Rdn;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
 import javax.swing.JPanel;
 
 import org.forgerock.i18n.LocalizableMessage;
@@ -125,6 +133,8 @@
 import org.opends.server.backends.task.TaskState;
 import org.opends.server.tools.BackendTypeHelper;
 import org.opends.server.tools.BackendTypeHelper.BackendTypeUIAdapter;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.FilePermission;
 import org.opends.server.types.HostPort;
 import org.opends.server.util.CertificateManager;
 import org.opends.server.util.CollectionUtils;
@@ -205,6 +215,8 @@
 
   private char[] selfSignedCertPw;
 
+  private ApplicationTrustManager trustManager;
+
   private boolean registeredNewServerOnRemote;
   private boolean createdAdministrator;
   private boolean createdRemoteAds;
@@ -1363,6 +1375,8 @@
       case PKCS11:
         configureKeyAndTrustStore(CertificateManager.KEY_STORE_PATH_PKCS11, CertificateManager.KEY_STORE_TYPE_PKCS11,
             CertificateManager.KEY_STORE_TYPE_JKS, sec);
+        configureAdminKeyAndTrustStore(CertificateManager.KEY_STORE_PATH_PKCS11, CertificateManager.KEY_STORE_TYPE_PKCS11,
+                CertificateManager.KEY_STORE_TYPE_JKS, sec);
         break;
 
       default:
@@ -1394,6 +1408,38 @@
     }
   }
 
+  private void configureAdminKeyAndTrustStore(final String keyStorePath, final String keyStoreType,
+      final String trustStoreType, final SecurityOptions sec) throws Exception
+  {
+    final String keystorePassword = sec.getKeystorePassword();
+    final String trustStorePath = getPath2("admin-truststore");
+
+    CertificateManager certManager = new CertificateManager(keyStorePath, keyStoreType, keystorePassword);
+    for (String keyStoreAlias : sec.getAliasesToUse())
+    {
+      SetupUtils.exportCertificate(certManager, keyStoreAlias, getTemporaryCertificatePath());
+      configureAdminTrustStore(trustStorePath, trustStoreType, keyStoreAlias, keystorePassword);
+    }
+
+    // Set default trustManager to allow check server startup status
+    if (com.forgerock.opendj.util.StaticUtils.isFips()) {
+        KeyStore truststore = null;
+        try (final FileInputStream fis = new FileInputStream(trustStorePath))
+        {
+          truststore = KeyStore.getInstance(trustStoreType);
+          truststore.load(fis, keystorePassword.toCharArray());
+        }
+        catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e)
+        {
+          // Nothing to do: if this occurs we will systematically refuse the certificates.
+          // Maybe we should avoid this and be strict, but we are in a best effort mode.
+          logger.warn(LocalizableMessage.raw("Error with the truststore"), e);
+        }
+
+        this.trustManager = new ApplicationTrustManager(truststore);
+    }
+  }
+
   private void configureTrustStore(final String type, final String keyStoreAlias, final String password)
       throws Exception
   {
@@ -1406,6 +1452,28 @@
     f.delete();
   }
 
+  private void configureAdminTrustStore(final String trustStorePath, final String type, final String keyStoreAlias, final String password)
+      throws Exception
+  {
+    final String alias = keyStoreAlias != null ? keyStoreAlias : SELF_SIGNED_CERT_ALIASES[0];
+    final CertificateManager trustMgr = new CertificateManager(trustStorePath, type, password);
+    trustMgr.addCertificate(alias, new File(getTemporaryCertificatePath()));
+
+    createProtectedFile(getKeystorePinPath(), password);
+    final File f = new File(getTemporaryCertificatePath());
+    f.delete();
+  }
+
+  @Override
+  public ApplicationTrustManager getTrustManager()
+  {
+	if (trustManager != null) {
+		return trustManager;
+	}
+
+	return super.getTrustManager();
+  }
+
   private void addCertificateArguments(SecurityOptions sec, List<String> argList)
   {
     final Collection<String> aliasInKeyStore = sec.getAliasesToUse();
@@ -4602,6 +4670,7 @@
     FileManager fileManager = new FileManager();
     fileManager.synchronize(getInstallation().getTemplateDirectory(), getInstallation().getInstanceDirectory());
   }
+
 }
 
 /** Class used to be able to cancel long operations. */
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/SetupLauncher.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/SetupLauncher.java
index cdeaa67..ff6820b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/SetupLauncher.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/installer/SetupLauncher.java
@@ -20,6 +20,8 @@
 import static org.opends.messages.ToolMessages.*;
 import static org.opends.server.util.ServerConstants.*;
 
+import static com.forgerock.opendj.util.StaticUtils.registerBcProvider;
+
 import org.forgerock.i18n.LocalizableMessage;
 import org.opends.quicksetup.CliApplication;
 import org.opends.quicksetup.Installation;
@@ -91,6 +93,8 @@
     try
     {
       argParser.parseArguments(args);
+      
+      registerBcProvider();
 
       if (argParser.isVersionArgumentPresent())
       {
diff --git a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
index a4ee88a..a0dfdbc 100644
--- a/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
+++ b/opendj-server-legacy/src/main/java/org/opends/quicksetup/util/ServerController.java
@@ -23,6 +23,8 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.net.ssl.TrustManager;
+
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -39,6 +41,7 @@
 import org.opends.server.util.SetupUtils;
 
 import com.forgerock.opendj.cli.CliConstants;
+import com.forgerock.opendj.util.StaticUtils;
 
 import static com.forgerock.opendj.cli.ArgumentConstants.*;
 import static com.forgerock.opendj.cli.Utils.*;
@@ -308,6 +311,7 @@
    * connect to the server after starting to verify that it is listening.
    * @param suppressOutput indicating that ouput to standard output streams
    * from the server should be suppressed.
+   * @param trustManager can be null
    * @throws org.opends.quicksetup.ApplicationException if something goes wrong.
    */
   private void startServer(boolean verifyCanConnect, boolean suppressOutput)
@@ -454,6 +458,11 @@
       userDn = null;
       userPw = null;
     }
+    
+    TrustManager trustManager = null;
+    if (StaticUtils.isFips()) {
+      trustManager = application.getTrustManager().getX509TrustManager();
+    }
 
     for (int i=0; i<50 && !connected; i++)
     {
@@ -463,7 +472,7 @@
         timeout = application.getUserData().getConnectTimeout();
       }
       HostPort hp = new HostPort(getHostName(i), port);
-      try (ConnectionWrapper conn = new ConnectionWrapper(hp, LDAPS, userDn, userPw, timeout, null))
+      try (ConnectionWrapper conn = new ConnectionWrapper(hp, LDAPS, userDn, userPw, timeout, trustManager))
       {
         return;
       }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
index 42fea94..ae4c812 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/config/ConfigurationHandler.java
@@ -589,10 +589,12 @@
    *          The original entry that is being replaced.
    * @param newEntry
    *          The new entry to use in place of the existing entry with the same DN.
+   * @param structuralUpdate
+   *          Force objectClass update.
    * @throws DirectoryException
    *           If a problem occurs while trying to replace the entry.
    */
-  public void replaceEntry(final Entry oldEntry, final Entry newEntry) throws DirectoryException
+  public void replaceEntry(final Entry oldEntry, final Entry newEntry, final boolean structuralUpdate) throws DirectoryException
   {
     final DN newEntryDN = newEntry.getName();
     if (!backend.contains(newEntryDN))
@@ -601,7 +603,7 @@
           ERR_CONFIG_FILE_MODIFY_NO_SUCH_ENTRY.get(oldEntry), getMatchedDN(newEntryDN), null);
     }
 
-    if (!Entries.getStructuralObjectClass(oldEntry, serverContext.getSchema()).equals(
+    if (!structuralUpdate && !Entries.getStructuralObjectClass(oldEntry, serverContext.getSchema()).equals(
         Entries.getStructuralObjectClass(newEntry, serverContext.getSchema())))
     {
       throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
@@ -657,6 +659,11 @@
     }
   }
 
+  public void replaceEntry(final Entry oldEntry, final Entry newEntry) throws DirectoryException
+  {
+	  replaceEntry(oldEntry, newEntry, false);
+  }
+
   @Override
   public void registerAddListener(final DN dn, final ConfigAddListener listener)
   {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
index 281fa0a..7c09ce0 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -27,6 +27,8 @@
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
+import static com.forgerock.opendj.util.StaticUtils.registerBcProvider;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -1403,6 +1405,7 @@
       // The core Directory Server configuration.
       coreConfigManager.initializeCoreConfig();
 
+      registerBcProvider();
       initializeCryptoManager();
 
       rotationPolicyConfigManager = new LogRotationPolicyConfigManager(serverContext);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedTrustManagerProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedTrustManagerProvider.java
index 3d74eac..f82d64c 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedTrustManagerProvider.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/FileBasedTrustManagerProvider.java
@@ -43,6 +43,8 @@
 import static org.opends.server.extensions.FileBasedKeyManagerProvider.getKeyStorePIN;
 import static org.opends.server.util.StaticUtils.*;
 
+import static com.forgerock.opendj.util.StaticUtils.isFips;
+
 /**
  * This class defines a trust manager provider that will reference certificates
  * stored in a file located on the Directory Server filesystem.
@@ -122,9 +124,13 @@
       trustManagerFactory.init(trustStore);
       TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
       TrustManager[] newTrustManagers = new TrustManager[trustManagers.length];
-      for (int i=0; i < trustManagers.length; i++)
-      {
-        newTrustManagers[i] = new ExpirationCheckTrustManager((X509TrustManager) trustManagers[i]);
+      if (isFips()) {
+    	  newTrustManagers = trustManagers;
+      } else {
+	      for (int i=0; i < trustManagers.length; i++)
+	      {
+	        newTrustManagers[i] = new ExpirationCheckTrustManager((X509TrustManager) trustManagers[i]);
+	      }
       }
       return newTrustManagers;
     }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/extensions/PKCS11TrustManagerProvider.java b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PKCS11TrustManagerProvider.java
new file mode 100644
index 0000000..e03490d
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/extensions/PKCS11TrustManagerProvider.java
@@ -0,0 +1,188 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2006-2010 Sun Microsystems, Inc.
+ * Portions Copyright 2021 Gluu, Inc.
+ */
+package org.opends.server.extensions;
+
+import org.forgerock.i18n.LocalizableMessage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.List;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.forgerock.opendj.config.server.ConfigurationChangeListener;
+import org.forgerock.opendj.server.config.server.TrustManagerProviderCfg;
+import org.forgerock.opendj.server.config.server.PKCS11TrustManagerProviderCfg;
+import org.opends.server.api.TrustManagerProvider;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.InitializationException;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.opends.server.util.ExpirationCheckTrustManager;
+
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import static org.opends.messages.ExtensionMessages.*;
+import static org.opends.server.extensions.FileBasedKeyManagerProvider.getKeyStorePIN;
+import static org.opends.server.util.StaticUtils.*;
+
+/**
+ * This class defines a trust manager provider that will reference certificates
+ * stored in a file located on the Directory Server filesystem.
+ */
+public class PKCS11TrustManagerProvider
+       extends TrustManagerProvider<PKCS11TrustManagerProviderCfg>
+       implements ConfigurationChangeListener<PKCS11TrustManagerProviderCfg>
+{
+  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
+  /** The truststore type to use when accessing the PKCS#11 keystore. */
+  private static final String PKCS11_TRUSTSTORE_TYPE = "PKCS11";
+
+  /** The PIN needed to access the trust store. */
+  private char[] trustStorePIN;
+
+  /** The handle to the configuration for this trust manager. */
+  private PKCS11TrustManagerProviderCfg currentConfig;
+
+  /**
+   * Creates a new instance of this file-based trust manager provider.  The
+   * <CODE>initializeTrustManagerProvider</CODE> method must be called on the
+   * resulting object before it may be used.
+   */
+  public PKCS11TrustManagerProvider()
+  {
+    // No implementation is required.
+  }
+
+  @Override
+  public void initializeTrustManagerProvider(PKCS11TrustManagerProviderCfg cfg)
+          throws ConfigException, InitializationException
+  {
+    final ConfigChangeResult ccr = new ConfigChangeResult();
+
+    currentConfig = cfg;
+    trustStorePIN = getTrustStorePIN(cfg, ccr);
+    if (!ccr.getMessages().isEmpty())
+    {
+      throw new InitializationException(ccr.getMessages().get(0));
+    }
+
+    cfg.addPKCS11ChangeListener(this);
+  }
+
+  @Override
+  public void finalizeTrustManagerProvider()
+  {
+    currentConfig.removePKCS11ChangeListener(this);
+  }
+
+  @Override
+  public TrustManager[] getTrustManagers() throws DirectoryException
+  {
+    KeyStore trustStore;
+    try {
+      trustStore = KeyStore.getInstance(PKCS11_TRUSTSTORE_TYPE);
+      trustStore.load(null, trustStorePIN);
+    }
+    catch (Exception e)
+    {
+      logger.traceException(e);
+      LocalizableMessage message = ERR_PKCS11_KEYMANAGER_CANNOT_LOAD.get(getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), message, e);
+    }
+
+    try
+    {
+      String trustManagerAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(trustManagerAlgorithm);
+      trustManagerFactory.init(trustStore);
+      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+
+      return trustManagers;
+    }
+    catch (Exception e)
+    {
+      logger.traceException(e);
+
+      LocalizableMessage message =
+    		  ERR_PKCS11_TRUSTMANAGER_CANNOT_CREATE_FACTORY.get(getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getCoreConfigManager().getServerErrorResultCode(), message, e);
+    }
+  }
+
+  @Override
+  public boolean isConfigurationAcceptable(TrustManagerProviderCfg cfg, List<LocalizableMessage> unacceptableReasons)
+  {
+    PKCS11TrustManagerProviderCfg config = (PKCS11TrustManagerProviderCfg) cfg;
+    return isConfigurationChangeAcceptable(config, unacceptableReasons);
+  }
+
+  @Override
+  public boolean isConfigurationChangeAcceptable(PKCS11TrustManagerProviderCfg cfg,
+                                                 List<LocalizableMessage> unacceptableReasons)
+  {
+    int startSize = unacceptableReasons.size();
+
+    final ConfigChangeResult ccr = new ConfigChangeResult();
+    getTrustStorePIN(cfg, ccr);
+    unacceptableReasons.addAll(ccr.getMessages());
+
+    return startSize == unacceptableReasons.size();
+  }
+
+  @Override
+  public ConfigChangeResult applyConfigurationChange(PKCS11TrustManagerProviderCfg cfg)
+  {
+    final ConfigChangeResult ccr = new ConfigChangeResult();
+    char[] newPIN = getTrustStorePIN(cfg, ccr);
+
+    if (ccr.getResultCode() == ResultCode.SUCCESS)
+    {
+      currentConfig = cfg;
+      trustStorePIN   = newPIN;
+    }
+
+    return ccr;
+  }
+
+  private char[] getTrustStorePIN(PKCS11TrustManagerProviderCfg cfg, ConfigChangeResult ccr)
+  {
+    try
+    {
+      return FileBasedKeyManagerProvider.getKeyStorePIN(cfg.getTrustStorePinProperty(),
+                            cfg.getTrustStorePinEnvironmentVariable(),
+                            cfg.getTrustStorePinFile(),
+                            cfg.getTrustStorePin(),
+                            cfg.dn(),
+                            ERR_FILE_TRUSTMANAGER_PIN_PROPERTY_NOT_SET,
+                            ERR_FILE_TRUSTMANAGER_PIN_ENVAR_NOT_SET,
+                            ERR_FILE_TRUSTMANAGER_PIN_NO_SUCH_FILE,
+                            ERR_FILE_TRUSTMANAGER_PIN_FILE_CANNOT_READ,
+                            ERR_FILE_TRUSTMANAGER_PIN_FILE_EMPTY);
+    }
+    catch (InitializationException e)
+    {
+      ccr.setResultCode(DirectoryServer.getCoreConfigManager().getServerErrorResultCode());
+      ccr.addMessage(e.getMessageObject());
+      return null;
+    }
+  }
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java
index a654e32..84990d9 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/ConfigureDS.java
@@ -85,6 +85,7 @@
 import com.forgerock.opendj.cli.FileBasedArgument;
 import com.forgerock.opendj.cli.IntegerArgument;
 import com.forgerock.opendj.cli.StringArgument;
+import com.forgerock.opendj.util.StaticUtils;
 
 /**
  * This class provides a very basic tool that can be used to configure some of
@@ -187,6 +188,8 @@
       + "ds-cfg-trust-store-type: JCEKS" + NEW_LINE
       + "ds-cfg-trust-store-file: config/truststore" + NEW_LINE;
 
+  private static final String DN_ADMIN_KEY_MANAGER = "cn=Administration,cn=Key Manager Providers," + DN_CONFIG_ROOT;
+
   /** The DN of the configuration entry defining the LDAP connection handler. */
   private static final String DN_LDAP_CONNECTION_HANDLER = "cn=LDAP Connection Handler," + DN_CONNHANDLER_BASE;
   /** The DN of the configuration entry defining the Administration connector. */
@@ -878,6 +881,9 @@
       putKeyManagerConfigAttribute(enableStartTLS, DN_LDAP_CONNECTION_HANDLER);
       putKeyManagerConfigAttribute(ldapsPort, DN_LDAPS_CONNECTION_HANDLER);
       putKeyManagerConfigAttribute(ldapsPort, DN_HTTP_CONNECTION_HANDLER);
+      if (StaticUtils.isFips()) {
+          putAdminKeyManagerConfigAttribute(ldapsPort, DN_ADMIN_KEY_MANAGER);
+      }
 
       if (keyManagerPath.isPresent())
       {
@@ -917,6 +923,39 @@
     }
   }
 
+  private void putAdminKeyManagerConfigAttribute(final Argument arg, final String attributeDN)
+      throws ConfigureDSException
+  {
+    if (arg.isPresent())
+    {
+      try
+      {
+        updateConfigEntryByRemovingAttribute(attributeDN, ATTR_KEYSTORE_TYPE);
+        updateConfigEntryByRemovingAttribute(attributeDN, ATTR_KEYSTORE_FILE);
+
+        updateConfigEntryWithObjectClasses(
+                attributeDN,
+                "top", "ds-cfg-pkcs11-key-manager-provider", "ds-cfg-key-manager-provider");
+
+        updateConfigEntryWithAttribute(
+            attributeDN,
+            ATTR_KEYMANAGER_CLASS,
+            CoreSchema.getDirectoryStringSyntax(),
+            "org.opends.server.extensions.PKCS11KeyManagerProvider");
+
+        updateConfigEntryWithAttribute(
+                attributeDN,
+                ATTR_KEYSTORE_PIN_FILE,
+                CoreSchema.getDirectoryStringSyntax(),
+                "config/keystore.pin");
+      }
+      catch (final Exception e)
+      {
+        throw new ConfigureDSException(e, ERR_CONFIGDS_CANNOT_UPDATE_KEYMANAGER_REFERENCE.get(e));
+      }
+    }
+  }
+
   private void updateTrustManager() throws ConfigureDSException
   {
     if (trustManagerProviderDN.isPresent())
@@ -1127,6 +1166,15 @@
     configHandler.replaceEntry(configEntry, Converters.from(newEntry));
   }
 
+  /** Update a config entry with the provided objectCLass parameters. */
+  private void updateConfigEntryWithObjectClasses(String entryDn, Object...objectCLasses)
+      throws DirectoryException, ConfigException
+  {
+    org.forgerock.opendj.ldap.Entry configEntry = configHandler.getEntry(DN.valueOf(entryDn));
+    final org.forgerock.opendj.ldap.Entry newEntry = putAttribute(configEntry, ATTR_OBJECTCLASS, CoreSchema.getOIDSyntax(), objectCLasses);
+    configHandler.replaceEntry(configEntry, newEntry, true);
+  }
+
   /**
    * Duplicate the provided entry, and put an attribute to the duplicated entry.
    * <p>
@@ -1158,7 +1206,7 @@
     {
       if (t.hasNameOrOID(attrName))
       {
-        entry.getUserAttributes().remove(t);
+    	duplicateEntry.getUserAttributes().remove(t);
         return duplicateEntry;
       }
     }
@@ -1167,7 +1215,7 @@
     {
       if (t.hasNameOrOID(attrName))
       {
-        entry.getOperationalAttributes().remove(t);
+    	duplicateEntry.getOperationalAttributes().remove(t);
         return duplicateEntry;
       }
     }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/tools/InstallDS.java b/opendj-server-legacy/src/main/java/org/opends/server/tools/InstallDS.java
index 487bfe5..8410788 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/tools/InstallDS.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/tools/InstallDS.java
@@ -825,7 +825,7 @@
       certType = SecurityOptions.CertificateType.NO_CERTIFICATE;
     }
 
-    Collection<String> certNicknames = argParser.certNicknameArg.getValues();
+    Collection<String> certNicknames = getCertNickNames();
     if (pathToCertificat != null)
     {
       checkCertificateInKeystore(certType, pathToCertificat, pwd, certNicknames, errorMessages, keystoreAliases);
@@ -840,6 +840,20 @@
     uData.setSecurityOptions(securityOptions);
   }
 
+  private List<String> getCertNickNames() {
+	  List<String> certNicknames = argParser.certNicknameArg.getValues();
+	  if ((certNicknames == null) || (certNicknames.size() == 0)) {
+		  return certNicknames;
+	  }
+
+	  List<String> splitedCertNicknames = new ArrayList<>();
+	  for (String certNickname : certNicknames) {
+		  splitedCertNicknames.addAll(StaticUtils.splittedStringAsList(certNickname, " "));
+	  }
+	  
+	  return splitedCertNicknames;
+  }
+
   private void checkCanUsePort(int port, List<LocalizableMessage> errorMessages)
   {
     if (!SetupUtils.canUseAsPort(port))
@@ -1943,7 +1957,7 @@
       boolean enableStartTLS, int ldapsPort) throws UserDataException, ClientException
   {
     String path;
-    Collection<String> certNicknames = argParser.certNicknameArg.getValues();
+    Collection<String> certNicknames = getCertNickNames();
     String pwd = argParser.getKeyStorePassword();
     if (pwd != null && pwd.length() == 0)
     {
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/util/StaticUtils.java b/opendj-server-legacy/src/main/java/org/opends/server/util/StaticUtils.java
index 14a6713..c20ee44 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/util/StaticUtils.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/util/StaticUtils.java
@@ -71,6 +71,8 @@
 
 import com.forgerock.opendj.cli.Argument;
 import com.forgerock.opendj.cli.ArgumentException;
+import java.security.Provider;
+import java.security.Security;
 
 /**
  * This class defines a number of static utility methods that may be used
@@ -2579,5 +2581,18 @@
       }
     }
   }
+
+  public static List<String> splittedStringAsList(String str, String delim) {
+      final List<String> result = new ArrayList<String>();
+      if ((str != null) && !str.isEmpty()) {
+          final String[] array = str.split(delim);
+          if (array.length > 0) {
+              result.addAll(Arrays.asList(array));
+          }
+      }
+
+      return result;
+  }
+
 }
 
diff --git a/opendj-server-legacy/src/messages/org/opends/messages/extension.properties b/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
index 62df785..84aa00c 100644
--- a/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
+++ b/opendj-server-legacy/src/messages/org/opends/messages/extension.properties
@@ -175,6 +175,9 @@
 ERR_FILE_TRUSTMANAGER_INVALID_TYPE_106=The trust store type %s \
  specified in attribute ds-cfg-trust-store-type of configuration entry %s is \
  not valid: %s
+ERR_PKCS11_TRUSTMANAGER_CANNOT_CREATE_FACTORY_107=An error occurred while \
+ trying to create a trust manager factory to access the contents of the PKCS#11 \
+ truststore: %s
 ERR_SEDCM_NO_PEER_CERTIFICATE_118=Could not map the provided certificate \
  chain to a user entry because no peer certificate was available
 ERR_SEDCM_PEER_CERT_NOT_X509_119=Could not map the provided certificate \

--
Gitblit v1.10.0