From 0e2118631c0ae4dc02e0904cec9bda8f399d412b Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 08 Mar 2007 17:28:22 +0000
Subject: [PATCH] Add a new CertificateManager class that can be used to simplify interacting with different types of key stores, including generating self-signed certificates and certificate signing requests.

---
 opendj-sdk/opends/src/server/org/opends/server/util/CertificateManager.java                                 |  860 ++++++++++++++++++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java | 1416 +++++++++++++++++++++++++++++++++++++
 2 files changed, 2,276 insertions(+), 0 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/CertificateManager.java b/opendj-sdk/opends/src/server/org/opends/server/util/CertificateManager.java
new file mode 100644
index 0000000..82961ca
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/CertificateManager.java
@@ -0,0 +1,860 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.util;
+
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+
+
+/**
+ * This class provides an interface for generating self-signed certificates and
+ * certificate signing requests, and for importing, exporting, and deleting
+ * certificates from a key store.  It supports JKS, PKCS11, and PKCS12 key store
+ * types.
+ * <BR><BR>
+ * Note that for some operations, particularly those that require updating the
+ * contents of a key store (including generating certificates and/or certificate
+ * signing  requests, importing certificates, or removing certificates), this
+ * class relies on the keytool utility provided with Sun's implementation of the
+ * Java runtime  environment.  It will perform the associated operations by
+ * invoking the appropriate command.  It is possible that the keytool command
+ * will not exist in all Java runtime environments, especially those not created
+ * by Sun.  In those cases, it will not be possible to invoke operations that
+ * require altering the contents of the key store.  Therefore, it is strongly
+ * recommended that any code that may want to make use of this facility should
+ * first call {@code mayUseCertificateManager} and if it returns {@code false}
+ * the caller should gracefully degrade and suggest that the user perform the
+ * operation manually.
+ */
+public class CertificateManager
+{
+  /**
+   * The path to the keytool command, which will be required to perform
+   * operations that modify the contents of a key store.
+   */
+  public static final String KEYTOOL_COMMAND;
+
+
+
+  /**
+   * The key store type value that should be used for the "JKS" key store.
+   */
+  public static final String KEY_STORE_TYPE_JKS = "JKS";
+
+
+
+  /**
+   * The key store type value that should be used for the "PKCS11" key store.
+   */
+  public static final String KEY_STORE_TYPE_PKCS11 = "PKCS11";
+
+
+
+  /**
+   * The key store type value that should be used for the "PKCS12" key store.
+   */
+  public static final String KEY_STORE_TYPE_PKCS12 = "PKCS12";
+
+
+
+  /**
+   * The key store path value that must be used in conjunction with the PKCS11
+   * key store type.
+   */
+  public static final String KEY_STORE_PATH_PKCS11 = "NONE";
+
+
+
+  // The parsed key store backing this certificate manager.
+  private KeyStore keyStore;
+
+  // The password that should be used to interact with the key store.
+  private String keyStorePIN;
+
+  // The path to the key store that we should be using.
+  private String keyStorePath;
+
+  // The name of the key store type we are using.
+  private String keyStoreType;
+
+
+
+  static
+  {
+    String keytoolCommand = null;
+
+    try
+    {
+      String cmd = System.getProperty("java.home") + File.separator + "bin" +
+                   File.separator + "keytool";
+      File cmdFile = new File(cmd);
+      if (cmdFile.exists())
+      {
+        keytoolCommand = cmdFile.getAbsolutePath();
+      }
+      else
+      {
+        cmd = cmd + ".exe";
+        cmdFile = new File(cmd);
+        if (cmdFile.exists())
+        {
+          keytoolCommand = cmdFile.getAbsolutePath();
+        }
+        else
+        {
+          keytoolCommand = null;
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      keytoolCommand = null;
+    }
+
+    KEYTOOL_COMMAND = keytoolCommand;
+  }
+
+
+
+  /**
+   * Indicates whether it is possible to use this certificate manager code to
+   * perform operations which may alter the contents of a key store.
+   *
+   * @return  {@code true} if it appears that the keytool utility is available
+   *          and may be used to execute commands that may alter the contents of
+   *          a key store, or {@code false} if not.
+   */
+  public static boolean mayUseCertificateManager()
+  {
+    return (KEYTOOL_COMMAND != null);
+  }
+
+
+
+  /**
+   * Creates a new certificate manager instance with the provided information.
+   *
+   * @param  keyStorePath  The path to the key store file, or "NONE" if the key
+   *                       store type is "PKCS11".  For the other key store
+   *                       types, the file does not need to exist if a new
+   *                       self-signed certificate or certificate signing
+   *                       request is to be generated, although the directory
+   *                       containing the file must exist.  The key store file
+   *                       must exist if import or export operations are to be
+   *                       performed.
+   * @param  keyStoreType  The key store type to use.  It should be one of
+   *                       {@code KEY_STORE_TYPE_JKS},
+   *                       {@code KEY_STORE_TYPE_PKCS11}, or
+   *                       {@code KEY_STORE_TYPE_PKCS12}.
+   * @param  keyStorePIN   The PIN required to access the key store.  It must
+   *                       not be {@code null}.
+   *
+   * @throws  IllegalArgumentException  If any of the provided arguments is
+   *                                    invalid.
+   *
+   * @throws  NullPointerException  If any of the provided arguments is
+   *                                {@code null}.
+   *
+   * @throws  UnsupportedOperationException  If it is not possible to use the
+   *                                         certificate manager on the
+   *                                         underlying platform.
+   */
+  public CertificateManager(String keyStorePath, String keyStoreType,
+                            String keyStorePIN)
+         throws IllegalArgumentException, NullPointerException,
+                UnsupportedOperationException
+  {
+    if ((keyStorePath == null) || (keyStorePath.length() == 0))
+    {
+      throw new NullPointerException("keyStorePath");
+    }
+    else if ((keyStoreType == null) || (keyStoreType.length() == 0))
+    {
+      throw new NullPointerException("keyStoreType");
+    }
+    else if ((keyStorePIN == null) || (keyStorePIN.length() == 0))
+    {
+      throw new NullPointerException("keyStorePIN");
+    }
+
+
+    if (keyStoreType.equals(KEY_STORE_TYPE_PKCS11))
+    {
+      if (! keyStorePath.equals(KEY_STORE_PATH_PKCS11))
+      {
+        // FIXME -- Make this an internationalizeable string.
+        throw new IllegalArgumentException("Invalid key store path for " +
+                                           "PKCS11 keystore -- it must be " +
+                                           KEY_STORE_PATH_PKCS11);
+      }
+    }
+    else if (keyStoreType.equals(KEY_STORE_TYPE_JKS) ||
+             keyStoreType.equals(KEY_STORE_TYPE_PKCS12))
+    {
+      File keyStoreFile = new File(keyStorePath);
+      if (keyStoreFile.exists())
+      {
+        if (! keyStoreFile.isFile())
+        {
+          // FIXME -- Make this an internationalizeable string.
+          throw new IllegalArgumentException("Key store path " + keyStorePath +
+                                             " exists but is not a file.");
+        }
+      }
+      else
+      {
+        File keyStoreDirectory = keyStoreFile.getParentFile();
+        if ((keyStoreDirectory == null) || (! keyStoreDirectory.exists()) ||
+            (! keyStoreDirectory.isDirectory()))
+        {
+          // FIXME -- Make this an internationalizeable string.
+          throw new IllegalArgumentException("Parent directory for key " +
+                         "store path " + keyStorePath + " does not exist or " +
+                         "is not a directory.");
+        }
+      }
+    }
+    else
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("Invalid key store type -- it must " +
+                  "be one of " + KEY_STORE_TYPE_JKS + ", " +
+                  KEY_STORE_TYPE_PKCS11 + ", or " + KEY_STORE_TYPE_PKCS12);
+    }
+
+
+    this.keyStorePath = keyStorePath;
+    this.keyStoreType = keyStoreType;
+    this.keyStorePIN  = keyStorePIN;
+
+    keyStore = null;
+  }
+
+
+
+  /**
+   * Indicates whether the provided alias is in use in the key store.
+   *
+   * @param  alias  The alias for which to make the determination.  It must not
+   *                be {@code null} or empty.
+   *
+   * @return  {@code true} if the key store exist and already contains a
+   *          certificate with the given alias, or {@code false} if not.
+   *
+   * @throws  KeyStoreException  If a problem occurs while attempting to
+   *                             interact with the key store.
+   *
+   * @throws  NullPointerException  If the provided alias is {@code null} or a
+   *                                zero-length string.
+   */
+  public boolean aliasInUse(String alias)
+         throws KeyStoreException, NullPointerException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+
+
+    KeyStore keyStore = getKeyStore();
+    if (keyStore == null)
+    {
+      return false;
+    }
+
+    return keyStore.containsAlias(alias);
+  }
+
+
+
+  /**
+   * Retrieves the aliases of the certificates in the specified key store.
+   *
+   * @return  The aliases of the certificates in the specified key store, or
+   *          {@code null} if the key store does not exist.
+   *
+   * @throws  KeyStoreException  If a problem occurs while attempting to
+   *                             interact with the key store.
+   */
+  public String[] getCertificateAliases()
+         throws KeyStoreException
+  {
+    KeyStore keyStore = getKeyStore();
+    if (keyStore == null)
+    {
+      return null;
+    }
+
+    Enumeration<String> aliasEnumeration = keyStore.aliases();
+    if (aliasEnumeration == null)
+    {
+      return new String[0];
+    }
+
+    ArrayList<String> aliasList = new ArrayList<String>();
+    while (aliasEnumeration.hasMoreElements())
+    {
+      aliasList.add(aliasEnumeration.nextElement());
+    }
+
+    String[] aliases = new String[aliasList.size()];
+    return aliasList.toArray(aliases);
+  }
+
+
+
+  /**
+   * Retrieves the certificate with the specified alias from the key store.
+   *
+   * @param  alias  The alias of the certificate to retrieve.  It must not be
+   *                {@code null} or empty.
+   *
+   * @return  The requested certificate, or {@code null} if the specified
+   *          certificate does not exist.
+   *
+   * @throws  KeyStoreException  If a problem occurs while interacting with the
+   *                             key store, or the key store does not exist.
+   *
+   * @throws  NullPointerException  If the provided alias is {@code null} or a
+   *                                zero-length string.
+   */
+  public Certificate getCertificate(String alias)
+         throws KeyStoreException, NullPointerException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+
+    KeyStore keyStore = getKeyStore();
+    if (keyStore == null)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new KeyStoreException("The key store does not exist.");
+    }
+
+    return keyStore.getCertificate(alias);
+  }
+
+
+
+  /**
+   * Generates a self-signed certificate using the provided information.
+   *
+   * @param  alias      The nickname to use for the certificate in the key
+   *                    store.  For the server certificate, it should generally
+   *                    be "server-cert".  It must not be {@code null} or empty.
+   * @param  subjectDN  The subject DN to use for the certificate.  It must not
+   *                    be {@code null} or empty.
+   * @param  validity   The length of time in days that the certificate should
+   *                    be valid, starting from the time the certificate is
+   *                    generated.  It must be a positive integer value.
+   *
+   * @throws  IllegalArgumentException  If the validity is not positive.
+   *
+   * @throws  KeyStoreException  If a problem occurs while actually attempting
+   *                             to generate the certificate in the key store.
+   *
+   * @throws  NullPointerException  If either the alias or subject DN is null or
+   *                                a zero-length string.
+   *
+   * @throws  UnsupportedOperationException  If it is not possible to use the
+   *                                         keytool utility to alter the
+   *                                         contents of the key store.
+   */
+  public void generateSelfSignedCertificate(String alias, String subjectDN,
+                                            int validity)
+         throws KeyStoreException, IllegalArgumentException,
+                NullPointerException, UnsupportedOperationException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+    else if ((subjectDN == null) || (subjectDN.length() == 0))
+    {
+      throw new NullPointerException("subjectDN");
+    }
+    else if (validity <= 0)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("The validity must be positive.");
+    }
+
+    if (KEYTOOL_COMMAND == null)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new UnsupportedOperationException("The certificate manager may " +
+                     "not be used to alter the contents of key stores on " +
+                     "this system.");
+    }
+
+    if (aliasInUse(alias))
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("A certificate with alias " + alias +
+                                         " already exists in the key store.");
+    }
+
+
+    // Clear the reference to the key store, since it will be altered by
+    // invoking the KeyTool command.
+    keyStore = null;
+
+
+    // First, we need to run with the "-genkey" command to create the private
+    // key.
+    String[] commandElements =
+    {
+      KEYTOOL_COMMAND,
+      "-genkey",
+      "-alias", alias,
+      "-dname", subjectDN.toString(),
+      "-keyalg", "rsa",
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, false);
+
+    // Next, we need to run with the "-selfcert" command to self-sign the
+    // certificate.
+    commandElements = new String[]
+    {
+      KEYTOOL_COMMAND,
+      "-selfcert",
+      "-alias", alias,
+      "-validity", String.valueOf(validity),
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, false);
+  }
+
+
+
+  /**
+   * Generates a certificate signing request (CSR) using the provided
+   * information.
+   *
+   * @param  alias      The nickname to use for the certificate in the key
+   *                    store.  For the server certificate, it should generally
+   *                    be "server-cert".  It must not be {@code null} or empty.
+   * @param  subjectDN  The subject DN to use for the certificate.  It must not
+   *                    be {@code null} or empty.
+   *
+   * @return  The file containing the generated certificate signing request.
+   *
+   * @throws  KeyStoreException  If a problem occurs while actually attempting
+   *                             to generate the private key in the key store or
+   *                             generate the certificate signing request based
+   *                             on that key.
+   *
+   * @throws  IOException  If a problem occurs while attempting to create the
+   *                       file to which the certificate signing request will be
+   *                       written.
+   *
+   * @throws  NullPointerException  If either the alias or subject DN is null or
+   *                                a zero-length string.
+   *
+   * @throws  UnsupportedOperationException  If it is not possible to use the
+   *                                         keytool utility to alter the
+   *                                         contents of the key store.
+   */
+  public File generateCertificateSigningRequest(String alias, String subjectDN)
+         throws KeyStoreException, IOException, NullPointerException,
+                UnsupportedOperationException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+    else if ((subjectDN == null) || (subjectDN.length() == 0))
+    {
+      throw new NullPointerException("subjectDN");
+    }
+
+    if (KEYTOOL_COMMAND == null)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new UnsupportedOperationException("The certificate manager may " +
+                     "not be used to alter the contents of key stores on " +
+                     "this system.");
+    }
+
+    if (aliasInUse(alias))
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("A certificate with alias " + alias +
+                                         " already exists in the key store.");
+    }
+
+
+    // Clear the reference to the key store, since it will be altered by
+    // invoking the KeyTool command.
+    keyStore = null;
+
+
+    // First, we need to run with the "-genkey" command to create the private
+    // key.
+    String[] commandElements =
+    {
+      KEYTOOL_COMMAND,
+      "-genkey",
+      "-alias", alias,
+      "-dname", subjectDN.toString(),
+      "-keyalg", "rsa",
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, false);
+
+    // Next, we need to run with the "-certreq" command to generate the
+    // certificate signing request.
+    File csrFile = File.createTempFile("CertificateManager-", ".csr");
+    commandElements = new String[]
+    {
+      KEYTOOL_COMMAND,
+      "-certreq",
+      "-alias", alias,
+      "-file", csrFile.getAbsolutePath(),
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, false);
+
+    return csrFile;
+  }
+
+
+
+  /**
+   * Adds the provided certificate to the key store.  This may be used to
+   * associate an externally-signed certificate with an existing private key
+   * with the given alias.
+   *
+   * @param  alias            The alias to use for the certificate.  It must not
+   *                          be {@code null} or empty.
+   * @param  certificateFile  The file containing the encoded certificate.  It
+   *                          must not be {@code null}, and the file must exist.
+   *
+   * @throws  IllegalArgumentException  If the provided certificate file does
+   *                                    not exist.
+   *
+   * @throws  KeyStoreException  If a problem occurs while interacting with the
+   *                             key store.
+   *
+   * @throws  NullPointerException  If the provided alias is {@code null} or a
+   *                                zero-length string, or the certificate file
+   *                                is {@code null}.
+   *
+   * @throws  UnsupportedOperationException  If it is not possible to use the
+   *                                         keytool utility to alter the
+   *                                         contents of the key store.
+   */
+  public void addCertificate(String alias, File certificateFile)
+         throws IllegalArgumentException, KeyStoreException,
+                NullPointerException, UnsupportedOperationException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+
+    if (certificateFile == null)
+    {
+      throw new NullPointerException("certificateFile");
+    }
+    else if ((! certificateFile.exists()) ||
+             (! certificateFile.isFile()))
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("Certificate file " +
+                                         certificateFile.getAbsolutePath() +
+                                         " does not exist or is not a file.");
+    }
+
+    if (KEYTOOL_COMMAND == null)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new UnsupportedOperationException("The certificate manager may " +
+                     "not be used to alter the contents of key stores on " +
+                     "this system.");
+    }
+
+
+    // Clear the reference to the key store, since it will be altered by
+    // invoking the KeyTool command.
+    keyStore = null;
+
+
+    String[] commandElements =
+    {
+      KEYTOOL_COMMAND,
+      "-import",
+      "-noprompt",
+      "-alias", alias,
+      "-file", certificateFile.getAbsolutePath(),
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, true);
+  }
+
+
+
+  /**
+   * Removes the specified certificate from the key store.
+   *
+   * @param  alias  The alias to use for the certificate to remove.  It must not
+   *                be {@code null} or an empty string, and it must exist in
+   *                the key store.
+   *
+   * @throws  IllegalArgumentException  If the specified certificate does not
+   *                                    exist in the key store.
+   *
+   * @throws  KeyStoreException  If a problem occurs while interacting with the
+   *                             key store.
+   *
+   * @throws  NullPointerException  If the provided alias is {@code null} or a
+   *                                zero-length string, or the certificate file
+   *                                is {@code null}.
+   *
+   * @throws  UnsupportedOperationException  If it is not possible to use the
+   *                                         keytool utility to alter the
+   *                                         contents of the key store.
+   */
+  public void removeCertificate(String alias)
+         throws IllegalArgumentException, KeyStoreException,
+                NullPointerException, UnsupportedOperationException
+  {
+    if ((alias == null) || (alias.length() == 0))
+    {
+      throw new NullPointerException("alias");
+    }
+
+    if (KEYTOOL_COMMAND == null)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new UnsupportedOperationException("The certificate manager may " +
+                     "not be used to alter the contents of key stores on " +
+                     "this system.");
+    }
+
+    if (! aliasInUse(alias))
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new IllegalArgumentException("There is no certificate with alias " +
+                                         alias + " in the key store.");
+    }
+
+
+    // Clear the reference to the key store, since it will be altered by
+    // invoking the KeyTool command.
+    keyStore = null;
+
+
+    String[] commandElements =
+    {
+      KEYTOOL_COMMAND,
+      "-delete",
+      "-alias", alias,
+      "-keystore", keyStorePath,
+      "-keypass", keyStorePIN,
+      "-storetype", keyStoreType,
+      "-storepass", keyStorePIN
+    };
+    runKeyTool(commandElements, false);
+  }
+
+
+
+  /**
+   * Attempts to run the keytool utility with the provided arguments.
+   *
+   * @param  commandElements   The command and arguments to execute.  The first
+   *                           element of the array must be the command, and the
+   *                           remaining elements must be the arguments.
+   * @param  outputAcceptable  Indicates whether it is acceptable for the
+   *                           command to generate output, as long as the exit
+   *                           code is zero.  Some commands (like "keytool
+   *                           -import") may generate output even on successful
+   *                           completion.  If the command generates output and
+   *                           this is {@code false}, then an exception will
+   *                           be thrown.
+   *
+   * @throws  KeyStoreException  If a problem occurs while attempting to invoke
+   *                             the keytool utility, if it does not exit with
+   *                             the expected exit code, or if any unexpected
+   *                             output is generated while running the tool.
+   */
+  private void runKeyTool(String[] commandElements, boolean outputAcceptable)
+          throws KeyStoreException
+  {
+    try
+    {
+      ProcessBuilder processBuilder = new ProcessBuilder(commandElements);
+      processBuilder.redirectErrorStream(true);
+
+      ByteArrayOutputStream output = new ByteArrayOutputStream();
+      byte[] buffer = new byte[1024];
+      Process process = processBuilder.start();
+      InputStream inputStream = process.getInputStream();
+      while (true)
+      {
+        int bytesRead = inputStream.read(buffer);
+        if (bytesRead < 0)
+        {
+          break;
+        }
+        else if (bytesRead > 0)
+        {
+          output.write(buffer, 0, bytesRead);
+        }
+      }
+
+      process.waitFor();
+      int exitValue = process.exitValue();
+      byte[] outputBytes = output.toByteArray();
+      if (exitValue != 0)
+      {
+        // FIXME -- Make this an internationalizeable string.
+        StringBuilder message = new StringBuilder();
+        message.append("Unexpected exit code of ");
+        message.append(exitValue);
+        message.append(" returned from the keytool utility.");
+
+        if ((outputBytes != null) && (outputBytes.length > 0))
+        {
+          message.append("  The generated output was:  '");
+          message.append(new String(outputBytes));
+          message.append("'.");
+        }
+
+        throw new KeyStoreException(message.toString());
+      }
+      else if ((! outputAcceptable) && (outputBytes != null) &&
+               (outputBytes.length > 0))
+      {
+        // FIXME -- Make this an internationalizeable string.
+        StringBuilder message = new StringBuilder();
+        message.append("Unexpected output generated by the keytool " +
+                       "utility:  '");
+        message.append(new String(outputBytes));
+        message.append("'.");
+
+        throw new KeyStoreException(message.toString());
+      }
+    }
+    catch (KeyStoreException kse)
+    {
+      throw kse;
+    }
+    catch (Exception e)
+    {
+      // FIXME -- Make this an internationalizeable string.
+      throw new KeyStoreException("Could not invoke the KeyTool.run method:  " +
+                                  e, e);
+    }
+  }
+
+
+
+  /**
+   * Retrieves a handle to the key store.
+   *
+   * @return  The handle to the key store, or {@code null} if the key store
+   *          doesn't exist.
+   *
+   * @throws  KeyStoreException  If a problem occurs while trying to open the
+   *                             key store.
+   */
+  private KeyStore getKeyStore()
+          throws KeyStoreException
+  {
+    if (keyStore != null)
+    {
+      return keyStore;
+    }
+
+    // For JKS and PKCS12 key stores, we should make sure the file exists, and
+    // we'll need an input stream that we can use to read it.  For PKCS11 key
+    // stores there won't be a file and the input stream should be null.
+    FileInputStream keyStoreInputStream = null;
+    if (keyStoreType.equals(KEY_STORE_TYPE_JKS) ||
+        keyStoreType.equals(KEY_STORE_TYPE_PKCS12))
+    {
+      File keyStoreFile = new File(keyStorePath);
+      if (! keyStoreFile.exists())
+      {
+        return null;
+      }
+
+      try
+      {
+        keyStoreInputStream = new FileInputStream(keyStoreFile);
+      }
+      catch (Exception e)
+      {
+        throw new KeyStoreException(String.valueOf(e), e);
+      }
+    }
+
+
+    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+    try
+    {
+      keyStore.load(keyStoreInputStream, keyStorePIN.toCharArray());
+      return this.keyStore = keyStore;
+    }
+    catch (Exception e)
+    {
+      throw new KeyStoreException(String.valueOf(e), e);
+    }
+  }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java
new file mode 100644
index 0000000..b08ba9f
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/util/CertificateManagerTestCase.java
@@ -0,0 +1,1416 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.util;
+
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of generic test cases for the certificate manager class.
+ */
+public class CertificateManagerTestCase
+       extends UtilTestCase
+{
+  /**
+   * Indicates whether the certificate manager is available on this platform and
+   * should be tested.
+   */
+  public static final boolean CERT_MANAGER_AVAILABLE =
+       CertificateManager.mayUseCertificateManager();
+
+
+
+  /**
+   * The path to a JKS key store file.
+   */
+  public static final String JKS_KEY_STORE_PATH =
+       System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT) + File.separator +
+       "build" + File.separator + "unit-tests" + File.separator + "package" +
+       File.separator + "config" + File.separator + "server.keystore";
+
+
+
+  /**
+   * The path to a PKCS#12 key store file.
+   */
+  public static final String PKCS12_KEY_STORE_PATH =
+       System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT) + File.separator +
+       "build" + File.separator + "unit-tests" + File.separator + "package" +
+       File.separator + "config" + File.separator + "server-cert.p12";
+
+
+
+  /**
+   * The path to the unit test working directory.
+   */
+  public static final String TEST_DIR =
+       System.getProperty(TestCaseUtils.PROPERTY_BUILD_ROOT) + File.separator +
+       "build" + File.separator + "unit-tests" + File.separator + "package";
+
+
+
+  /**
+   * Make sure the server is running.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @BeforeClass()
+  public void startServer()
+         throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using a null argument for the key
+   * store path.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorNullPath()
+         throws Exception
+  {
+    new CertificateManager(null, "JKS", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using an empty string for the key
+   * store path.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorEmptyPath()
+         throws Exception
+  {
+    new CertificateManager("", "JKS", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using a key store path that refers
+   * to a file in a nonexistent directory.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructorNonexistentPath()
+         throws Exception
+  {
+    String path = TEST_DIR + File.separator + "nonexistent" + File.separator +
+                  "doesntmatter";
+
+    new CertificateManager(path, "JKS", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using a key store path that refers
+   * to a file that exists but isn't a file.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructorPathNotFile()
+         throws Exception
+  {
+    new CertificateManager(TEST_DIR, "JKS", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using a null argument for the key
+   * store type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorNullType()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, null, "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using an empty string for the key
+   * store type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorEmptyType()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, "", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using an invalid key store type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructorInvalidType()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, "invalid", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using an invalid key store path
+   * in conjunction with the PKCS11 key store type..
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { IllegalArgumentException.class })
+  public void testConstructorInvalidPKCS11Path()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, "PKCS11", "password");
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using a null argument for the key
+   * store PIN.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorNullPIN()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, "JKS", null);
+  }
+
+
+
+  /**
+   * Tests the CertificateManager constructor using an empty string for the key
+   * store PIN.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test(expectedExceptions = { NullPointerException.class })
+  public void testConstructorEmptyPIN()
+         throws Exception
+  {
+    new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "");
+  }
+
+
+
+  /**
+   * Tests the certificate manager with a valid constructor using the JKS key
+   * store type.
+   */
+  @Test()
+  public void testValidConstructorJKS()
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+  }
+
+
+
+  /**
+   * Tests the certificate manager with a valid constructor using the PKCS12 key
+   * store type.
+   */
+  @Test()
+  public void testValidConstructorPKCS12()
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    new CertificateManager(PKCS12_KEY_STORE_PATH, "PKCS12", "password");
+  }
+
+
+
+  /**
+   * Tests the {@code aliasInUse} method with a null alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAliasInUseNull()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.aliasInUse(null);
+      fail("Expected an NPE due to null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code aliasInUse} method with an empty alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAliasInUseEmpty()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.aliasInUse("");
+      fail("Expected an NPE due to empty alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code aliasInUse} method with an invalid key store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAliasInUseInvalidKeyStore()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testAliasInUseInvalidKeyStore",
+                                    ".notakeystore");
+    path.deleteOnExit();
+    FileOutputStream outputStream = new FileOutputStream(path, false);
+    outputStream.write("This is not a valid key store.".getBytes());
+    outputStream.close();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+
+    try
+    {
+      certManager.aliasInUse("doesntmatter");
+      fail("Expected a key store exception due to an invalid key store");
+    } catch (KeyStoreException kse) {}
+
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code aliasInUse} method for a key store using the JKS type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAliasInUseJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+    assertTrue(certManager.aliasInUse("server-cert"));
+    assertFalse(certManager.aliasInUse("nonexistent"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "JKS", "password");
+    assertFalse(certManager.aliasInUse("doesntmatter"));
+  }
+
+
+
+  /**
+   * Tests the {@code aliasInUse} method for a key store using the PKCS12 type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAliasInUsePKCS12()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(PKCS12_KEY_STORE_PATH, "PKCS12", "password");
+    assertTrue(certManager.aliasInUse("server-cert"));
+    assertFalse(certManager.aliasInUse("nonexistent"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "PKCS12", "password");
+    assertFalse(certManager.aliasInUse("doesntmatter"));
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificateAliases} method for a key store using the
+   * JKS type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificateAliasesJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    String[] aliases = certManager.getCertificateAliases();
+    assertNotNull(aliases);
+    assertTrue(aliases.length > 0);
+    assertTrue(Arrays.asList(aliases).contains("server-cert"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "JKS", "password");
+    assertNull(certManager.getCertificateAliases());
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificateAliases} method for a key store using the
+   * PKCS12 type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificateAliasesPKCS12()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(PKCS12_KEY_STORE_PATH, "PKCS12", "password");
+
+    String[] aliases = certManager.getCertificateAliases();
+    assertNotNull(aliases);
+    assertTrue(aliases.length > 0);
+    assertTrue(Arrays.asList(aliases).contains("server-cert"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "PKCS12", "password");
+    assertNull(certManager.getCertificateAliases());
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificate} method using a null alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificateNull()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.getCertificate(null);
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificate} method using an empty alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificateEmpty()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.getCertificate("");
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificate} method for a key store using the JKS type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificateJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+    assertNotNull(certManager.getCertificate("server-cert"));
+    assertNull(certManager.getCertificate("nonexistent"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "JKS", "password");
+    try
+    {
+      certManager.getCertificate("doesntmatter");
+      fail("Expected a key store exception due to a nonexistent key store");
+    } catch (KeyStoreException kse) {}
+  }
+
+
+
+  /**
+   * Tests the {@code getCertificate} method for a key store using the PKCS12
+   * type.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGetCertificatePKCS12()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(PKCS12_KEY_STORE_PATH, "PKCS12", "password");
+    assertNotNull(certManager.getCertificate("server-cert"));
+    assertNull(certManager.getCertificate("nonexistent"));
+
+    String path = TEST_DIR + File.separator + "nonexistent";
+    certManager = new CertificateManager(path, "PKCS12", "password");
+    try
+    {
+      certManager.getCertificate("doesntmatter");
+      fail("Expected a key store exception due to a nonexistent key store");
+    } catch (KeyStoreException kse) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using a null alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateNullAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate(null, "CN=Test,O=test", 365);
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using an empty
+   * alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateEmptyAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate("", "CN=Test,O=test", 365);
+      fail("Expected an NPE due to an empty alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using an alias
+   * that's already being used.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateAliasInUse()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate("server-cert", "CN=Test,O=test",
+                                                365);
+      fail("Expected an illegal argument exception to a duplicate alias");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using a null
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateNullSubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate("test-cert", null, 365);
+      fail("Expected an NPE due to a null subject");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using an empty
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateEmptySubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate("test-cert", "", 365);
+      fail("Expected an NPE due to an empty subject");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using an invalid
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateInvalidSubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateSelfSignedCertificateJKS",
+                                    ".keystore");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+    try
+    {
+      certManager.generateSelfSignedCertificate("test-cert", "invalid", 365);
+      fail("Expected a key store exception due to an invalid subject");
+    } catch (KeyStoreException cse) {}
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method using an invalid
+   * validity.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateInvalidValidity()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateSelfSignedCertificate("test-cert", "CN=Test,o=test",
+                                                0);
+      fail("Expected an illegal argument exception due to an invalid validity");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method for a JKS key store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificateJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateSelfSignedCertificateJKS",
+                                    ".keystore");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+    certManager.generateSelfSignedCertificate("test-cert", "CN=Test,o=test",
+                                              365);
+    assertTrue(certManager.aliasInUse("test-cert"));
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code generateSelfSignedCertificate} method for a PKCS12 key
+   * store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateSelfSignedCertificatePKCS12()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateSelfSignedCertificatePKCS12",
+                                    ".p12");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "PKCS12", "password");
+    certManager.generateSelfSignedCertificate("test-cert", "CN=Test,o=test",
+                                              365);
+    assertTrue(certManager.aliasInUse("test-cert"));
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using a null
+   * alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRNullAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateCertificateSigningRequest(null, "CN=Test,O=test");
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using an empty
+   * alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSREmptyAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateCertificateSigningRequest("", "CN=Test,O=test");
+      fail("Expected an NPE due to an empty alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using an alias
+   * that's already being used.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRAliasInUse()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateCertificateSigningRequest("server-cert",
+                                                    "CN=Test,O=test");
+      fail("Expected an illegal argument exception to a duplicate alias");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using a null
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRNullSubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateCertificateSigningRequest("test-cert", null);
+      fail("Expected an NPE due to a null subject");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using an empty
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSREmptySubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.generateCertificateSigningRequest("test-cert", "");
+      fail("Expected an NPE due to an empty subject");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method using an invalid
+   * subject.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRInvalidSubject()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateCSRJKS",
+                                    ".keystore");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+
+    try
+    {
+      File requestFile =
+           certManager.generateCertificateSigningRequest("test-cert",
+                                                         "invalid");
+      requestFile.delete();
+      fail("Expected a key store exception due to an invalid subject");
+    } catch (KeyStoreException cse) {}
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method for a JKS key
+   * store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateCSRJKS",
+                                    ".keystore");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+    File csrFile = certManager.generateCertificateSigningRequest("test-cert",
+                                    "CN=Test,o=test");
+    assertNotNull(csrFile);
+    assertTrue(csrFile.length() > 0);
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code generateCertificateSigningRequest} method for a PKCS12 key
+   * store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testGenerateCSRPKCS12()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testGenerateCSRPKCS12",
+                                    ".p12");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "PKCS12", "password");
+    File csrFile = certManager.generateCertificateSigningRequest("test-cert",
+                                    "CN=Test,o=test");
+    assertNotNull(csrFile);
+    assertTrue(csrFile.length() > 0);
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using a null alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateNullAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+    File exportFile = exportCertificate();
+
+    try
+    {
+      certManager.addCertificate(null, exportFile);
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+
+    exportFile.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using an empty alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateEmptyAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+    File exportFile = exportCertificate();
+
+    try
+    {
+      certManager.addCertificate("", exportFile);
+      fail("Expected an NPE due to an empty alias");
+    } catch (NullPointerException npe) {}
+
+    exportFile.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using a null certificate file.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateNullCertificateFile()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.addCertificate("test-cert", null);
+      fail("Expected an NPE due to a null certificate file");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using a certificate file that does
+   * not exist.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateMissingCertificateFile()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    File f = new File(TEST_DIR, "nonexistent");
+
+    try
+    {
+      certManager.addCertificate("test-cert", f);
+      fail("Expected an illegal argument exception due to a missing " +
+           "certificate file");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using a certificate file that is
+   * not a file.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateFileNotFile()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    File f = new File(TEST_DIR);
+
+    try
+    {
+      certManager.addCertificate("test-cert", f);
+      fail("Expected an illegal argument exception due to a certificate file " +
+           "actually being a directory");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} method using a certificate file that
+   * contains something other than a certificate.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddCertificateFileNotCertificate()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    File path = File.createTempFile("testAddCertificateFileNotCertificate",
+                                    ".notacertificate");
+    path.deleteOnExit();
+    FileOutputStream outputStream = new FileOutputStream(path, false);
+    outputStream.write("This is not a valid certificate.".getBytes());
+    outputStream.close();
+
+    try
+    {
+      certManager.addCertificate("test-cert", path);
+      fail("Expected a key store exception due to an invalid certificate");
+    } catch (KeyStoreException kse) {}
+
+    path.delete();
+  }
+
+
+
+  /**
+   * Tests the {@code removeCertificate} method using a null alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testRemoveCertificateNullAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.removeCertificate(null);
+      fail("Expected an NPE due to a null alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code removeCertificate} method using an empty alias.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testRemoveCertificateEmptyAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.removeCertificate("");
+      fail("Expected an NPE due to an empty alias");
+    } catch (NullPointerException npe) {}
+  }
+
+
+
+  /**
+   * Tests the {@code removeCertificate} method using an alias that doesn't
+   * exist.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testRemoveCertificateNonexistentAlias()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    try
+    {
+      certManager.removeCertificate("nonexistent");
+      fail("Expected an illegal argument exception due to a nonexistent alias");
+    } catch (IllegalArgumentException iae) {}
+  }
+
+
+
+  /**
+   * Tests the {@code addCertificate} and {@code removeCertificate} methods
+   * using a newly-created JKS key store.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  @Test()
+  public void testAddAndRemoveCertificateJKS()
+         throws Exception
+  {
+    if (! CERT_MANAGER_AVAILABLE)
+    {
+      return;
+    }
+
+    File path = File.createTempFile("testAddAndRemoveCertificateJKS",
+                                    ".keystore");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(path.getAbsolutePath(), "JKS", "password");
+
+    File exportFile = exportCertificate();
+    certManager.addCertificate("test-cert", exportFile);
+    assertTrue(certManager.aliasInUse("test-cert"));
+    assertNotNull(certManager.getCertificate("test-cert"));
+
+    certManager.removeCertificate("test-cert");
+    assertFalse(certManager.aliasInUse("test-cert"));
+    assertNull(certManager.getCertificate("test-cert"));
+
+    exportFile.delete();
+    path.delete();
+  }
+
+
+
+  /**
+   * Exports a certificate to a temporary file.
+   *
+   * @throws  Exception  If a problem occurs.
+   */
+  private File exportCertificate()
+          throws Exception
+  {
+    File path = File.createTempFile("exportCertificate",
+                                    ".cert");
+    path.deleteOnExit();
+    path.delete();
+
+    CertificateManager certManager =
+         new CertificateManager(JKS_KEY_STORE_PATH, "JKS", "password");
+
+    Certificate certificate = certManager.getCertificate("server-cert");
+    assertNotNull(certificate);
+
+    byte[] certificateBytes = certificate.getEncoded();
+    assertNotNull(certificateBytes);
+    assertTrue(certificateBytes.length > 0);
+
+    FileOutputStream outputStream = new FileOutputStream(path, false);
+    outputStream.write(certificateBytes);
+    outputStream.close();
+
+    return path;
+  }
+}
+

--
Gitblit v1.10.0