From 7c2b635c8f01e96595090906b14d5a64d3d20c92 Mon Sep 17 00:00:00 2001
From: Peter Major <peter.major@forgerock.com>
Date: Fri, 06 May 2016 06:43:59 +0000
Subject: [PATCH] OPENDJ-2923 Use JVM's keyManager by default in SSLContextBuilder

---
 opendj-core/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java       |   81 ++++++++++++++++++++++++++++++++++++++--
 opendj-core/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java |   16 +++++---
 2 files changed, 86 insertions(+), 11 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java
index 39f5573..9957447 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/KeyManagers.java
@@ -38,6 +38,13 @@
 
 /** This class contains methods for creating common types of key manager. */
 public final class KeyManagers {
+
+    private static final String KEY_STORE_PROVIDER = "javax.net.ssl.keyStoreProvider";
+    private static final String KEY_STORE_TYPE = "javax.net.ssl.keyStoreType";
+    private static final String KEY_STORE_FILE = "javax.net.ssl.keyStore";
+    private static final String KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword";
+    private static volatile X509KeyManager jvmKeyManager = null;
+
     /**
      * This class implements an X.509 key manager that will be used to wrap an
      * existing key manager and makes it possible to configure which
@@ -189,18 +196,51 @@
      */
     public static X509KeyManager useKeyStoreFile(final String file, final char[] password,
             final String format) throws GeneralSecurityException, IOException {
+        return useKeyStoreFile(file, password, format, null);
+    }
+
+    /**
+     * Creates a new {@code X509KeyManager} which will use the named key store
+     * file for retrieving certificates. It will use the provided key store
+     * format and password.
+     *
+     * @param file
+     *            The key store file name.
+     * @param password
+     *            The key store password, which may be {@code null}.
+     * @param format
+     *            The key store format, which may be {@code null} to indicate that the default key store format for the
+     *            JVM (e.g. {@code JKS}) should be used.
+     * @param provider
+     *            The key store provider, which may be {@code null} to indicate that the default key store provider for
+     *            the JVM should be used.
+     * @return A new {@code X509KeyManager} which will use the named key store file for retrieving certificates.
+     * @throws GeneralSecurityException
+     *            If the key store could not be loaded, perhaps due to incorrect format, or missing algorithms.
+     * @throws IOException
+     *            If the key store file could not be found or could not be read.
+     * @throws NullPointerException
+     *            If {@code file} was {@code null}.
+     */
+    public static X509KeyManager useKeyStoreFile(final String file, final char[] password,
+            final String format, String provider) throws GeneralSecurityException, IOException {
         Reject.ifNull(file);
 
         final File keyStoreFile = new File(file);
         final String keyStoreFormat = format != null ? format : KeyStore.getDefaultType();
 
-        final KeyStore keyStore = KeyStore.getInstance(keyStoreFormat);
-        try (FileInputStream fos = new FileInputStream(keyStoreFile)) {
-            keyStore.load(fos, password);
+        final KeyStore keyStore;
+        if (provider != null) {
+            keyStore = KeyStore.getInstance(keyStoreFormat, provider);
+        } else {
+            keyStore = KeyStore.getInstance(keyStoreFormat);
         }
 
-        final KeyManagerFactory kmf =
-                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        try (FileInputStream fis = new FileInputStream(keyStoreFile)) {
+            keyStore.load(fis, password);
+        }
+
+        final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
         kmf.init(keyStore, password);
 
         X509KeyManager x509km = null;
@@ -257,6 +297,37 @@
     }
 
     /**
+     * Creates a new {@code X509KeyManager} which will use the JVM's default keystore for retrieving certificates.
+     *
+     * @return A new {@code X509KeyManager} which will use the JVM's default keystore for retrieving certificates or
+     *             {@code null} if the necessary JVM settings are missing.
+     * @throws GeneralSecurityException
+     *             If the key store could not be loaded, perhaps due to incorrect format, or missing algorithms.
+     * @throws IOException
+     *             If the key store file could not be found or could not be read.
+     */
+    public static X509KeyManager useJvmDefaultKeyStore() throws GeneralSecurityException, IOException {
+        if (jvmKeyManager == null) {
+            final String keyStoreFile = System.getProperty(KEY_STORE_FILE);
+            if (keyStoreFile != null) {
+                synchronized (KeyManagers.class) {
+                    if (jvmKeyManager == null) {
+                        final String keyStoreProvider = System.getProperty(KEY_STORE_PROVIDER);
+                        final String keyStoreType = System.getProperty(KEY_STORE_TYPE, KeyStore.getDefaultType());
+                        final String keyStorePassword = System.getProperty(KEY_STORE_PASSWORD);
+
+                        jvmKeyManager = useKeyStoreFile(keyStoreFile,
+                                keyStorePassword != null ? keyStorePassword.toCharArray() : null,
+                                keyStoreType, keyStoreProvider);
+                    }
+                }
+            }
+        }
+
+        return jvmKeyManager;
+    }
+
+    /**
      * Returns a new {@code X509KeyManager} which selects the named certificate
      * from the provided {@code X509KeyManager}.
      *
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java
index 169d966..fbacf49 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/SSLContextBuilder.java
@@ -16,6 +16,7 @@
  */
 package org.forgerock.opendj.ldap;
 
+import java.io.IOException;
 import java.security.GeneralSecurityException;
 import java.security.Provider;
 import java.security.SecureRandom;
@@ -80,7 +81,11 @@
 
     /** Creates a new SSL context builder using default parameters. */
     public SSLContextBuilder() {
-        // Do nothing.
+        try {
+            keyManager = KeyManagers.useJvmDefaultKeyStore();
+        } catch (GeneralSecurityException | IOException ex) {
+            keyManager = null;
+        }
     }
 
     /**
@@ -101,7 +106,7 @@
 
         KeyManager[] km = null;
         if (keyManager != null) {
-            km = new KeyManager[] { keyManager };
+            km = new KeyManager[]{keyManager};
         }
 
         SSLContext sslContext;
@@ -118,12 +123,11 @@
     }
 
     /**
-     * Sets the key manager which the SSL context should use. By default, no key
-     * manager is specified indicating that no certificates will be used.
+     * Sets the key manager which the SSL context should use. By default, the JVM's key manager is used.
      *
      * @param keyManager
-     *            The key manager which the SSL context should use, which may be
-     *            {@code null} indicating that no certificates will be used.
+     *            The key manager which the SSL context should use, which may be {@code null} indicating that no
+     *            certificates will be used.
      * @return This SSL context builder.
      */
     public SSLContextBuilder setKeyManager(final KeyManager keyManager) {

--
Gitblit v1.10.0