From 73b1d8214bd973d9ea1eb2def1ea1121b53ea5da Mon Sep 17 00:00:00 2001
From: coulbeck <coulbeck@localhost>
Date: Tue, 28 Aug 2007 16:04:25 +0000
Subject: [PATCH] Files missing from previous commit

---
 opends/src/server/org/opends/server/backends/TrustStoreBackend.java                  | 1619 ++++++++++++++++++++++++++++++++++++++++++++++++++
 opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml |  115 +++
 opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml     |  145 ++++
 3 files changed, 1,879 insertions(+), 0 deletions(-)

diff --git a/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml
new file mode 100644
index 0000000..41a6af2
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ! 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.
+ ! -->
+
+<adm:managed-object name="crypto-manager"
+  plural-name="crypto-managers"
+  package="org.opends.server.admin.std"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    The
+    <adm:user-friendly-name />
+    provides hashing, encryption and other kinds of cryptographic operations.
+    It also contains methods for compressing and uncompressing data.
+  </adm:synopsis>
+  <adm:tag name="security"/>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:oid>ds-cfg-crypto-manager-oid</ldap:oid>
+      <ldap:name>ds-cfg-crypto-manager</ldap:name>
+      <ldap:superior>top</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:property name="ssl-protocols" multi-valued="true">
+    <adm:synopsis>
+      Specifies the names of the SSL protocols that will be allowed for
+      use in SSL or TLS communication.
+    </adm:synopsis>
+    <adm:requires-admin-action>
+      <adm:none>
+        <adm:synopsis>
+          Changes to this property will take effect immediately but will
+          only impact new SSL/TLS-based sessions created after the
+          change.
+        </adm:synopsis>
+      </adm:none>
+    </adm:requires-admin-action>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          Uses the default set of SSL protocols provided by the server's
+          JVM.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.461</ldap:oid>
+        <ldap:name>ds-cfg-ssl-protocol</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="ssl-cipher-suites" multi-valued="true">
+    <adm:synopsis>
+      Specifies the names of the SSL cipher suites that will be allowed
+      for use in SSL or TLS communication.
+    </adm:synopsis>
+    <adm:requires-admin-action>
+      <adm:none>
+        <adm:synopsis>
+          Changes to this property will take effect immediately but will
+          only impact new SSL/TLS-based sessions created after the
+          change.
+        </adm:synopsis>
+      </adm:none>
+    </adm:requires-admin-action>
+    <adm:default-behavior>
+      <adm:alias>
+        <adm:synopsis>
+          Uses the default set of SSL cipher suites provided by the
+          server's JVM.
+        </adm:synopsis>
+      </adm:alias>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.462</ldap:oid>
+        <ldap:name>ds-cfg-ssl-cipher-suite</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="ssl-encryption" multi-valued="false">
+    <adm:synopsis>
+      Specifies whether SSL/TLS is used to provide encrypted communication
+      between two OpenDS server components.
+    </adm:synopsis>
+    <adm:requires-admin-action>
+      <adm:none>
+        <adm:synopsis>
+          Changes to this property will take effect immediately but will
+          only impact new SSL/TLS-based sessions created after the
+          change.
+        </adm:synopsis>
+      </adm:none>
+    </adm:requires-admin-action>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          false
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>ds-cfg-ssl-encryption-oid</ldap:oid>
+        <ldap:name>ds-cfg-ssl-encryption</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property-reference name="ssl-cert-nickname" />
+</adm:managed-object>
+
diff --git a/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml b/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml
new file mode 100644
index 0000000..e436def
--- /dev/null
+++ b/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ! 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.
+ ! -->
+
+<adm:managed-object name="trust-store-backend"
+  plural-name="trust-store-backends"
+  extends="backend"
+  package="org.opends.server.admin.std"
+  xmlns:adm="http://www.opends.org/admin"
+  xmlns:ldap="http://www.opends.org/admin-ldap">
+  <adm:synopsis>
+    The
+    <adm:user-friendly-name />
+    provides an LDAP view of a file-based trust store. It is used by the
+    administrative cryptographic framework.
+  </adm:synopsis>
+  <adm:profile name="ldap">
+    <ldap:object-class>
+      <ldap:oid>ds-cfg-trust-store-backend-oid</ldap:oid>
+      <ldap:name>ds-cfg-trust-store-backend</ldap:name>
+      <ldap:superior>ds-cfg-backend</ldap:superior>
+    </ldap:object-class>
+  </adm:profile>
+  <adm:property-override name="backend-class">
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          org.opends.server.backends.TrustStoreBackend
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+  </adm:property-override>
+  <adm:property name="trust-store-file" mandatory="true">
+    <adm:TODO>Should use a file-based property definition?</adm:TODO>
+    <adm:synopsis>
+      Specifies the path to the file that stores the trust
+      information. It may be an absolute path, or a path that is
+      relative to the
+      <adm:product-name />
+      instance root.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>
+          config/ads-truststore
+        </adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.129</ldap:oid>
+        <ldap:name>ds-cfg-trust-store-file</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <adm:property name="trust-store-type">
+    <adm:TODO>
+      Can we restrict this to an enumeration? How can the client guess
+      which values are possible? What is the default value?
+    </adm:TODO>
+    <adm:synopsis>
+      Specifies the format for the data in the key store file.
+    </adm:synopsis>
+    <adm:description>
+      Valid values should always include 'JKS' and 'PKCS12', but
+      different implementations may allow other values as well. If no
+      value is provided, then the JVM-default value will be used.
+      Changes to this configuration attribute will take effect the next
+      time that the key manager is accessed.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:undefined />
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:oid>1.3.6.1.4.1.26027.1.1.134</ldap:oid>
+        <ldap:name>ds-cfg-trust-store-type</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+  <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/opends/src/server/org/opends/server/backends/TrustStoreBackend.java b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
new file mode 100644
index 0000000..14ec3cb
--- /dev/null
+++ b/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -0,0 +1,1619 @@
+/*
+ * 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 2006-2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.backends;
+
+
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.FileOutputStream;
+import java.util.*;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+
+import org.opends.server.api.Backend;
+import org.opends.server.api.TrustManagerProvider;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+
+import static org.opends.server.config.ConfigConstants.*;
+import static org.opends.server.loggers.debug.DebugLogger.*;
+import org.opends.server.loggers.debug.DebugTracer;
+import org.opends.server.loggers.ErrorLogger;
+import org.opends.server.types.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
+import org.opends.server.util.Validator;
+import org.opends.server.util.CertificateManager;
+import org.opends.server.admin.server.ConfigurationChangeListener;
+import org.opends.server.admin.std.server.TrustStoreBackendCfg;
+import org.opends.server.admin.Configuration;
+import org.opends.server.extensions.BlindTrustManagerProvider;
+import org.opends.messages.Message;
+import static org.opends.messages.BackendMessages.*;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManager;
+import javax.naming.ldap.Rdn;
+import java.security.cert.Certificate;
+import java.net.UnknownHostException;
+
+
+/**
+ * This class defines a backend used to provide an LDAP view of public keys
+ * stored in a key store.
+ */
+public class TrustStoreBackend
+     extends Backend
+       implements ConfigurationChangeListener<TrustStoreBackendCfg>
+{
+  /**
+   * The tracer object for the debug logger.
+   */
+  private static final DebugTracer TRACER = getTracer();
+
+  // The current configuration state.
+  private TrustStoreBackendCfg configuration;
+
+  // The DN for the base entry.
+  private DN baseDN;
+
+  // The set of base DNs for this backend.
+  private DN[] baseDNs;
+
+  // The base entry.
+  private Entry baseEntry;
+
+  // The set of supported controls for this backend.
+  private HashSet<String> supportedControls;
+
+  // The set of supported features for this backend.
+  private HashSet<String> supportedFeatures;
+
+  // The PIN needed to access the trust store backing file.
+  private char[] trustStorePIN;
+
+  // The path to the trust store backing file.
+  private String trustStoreFile;
+
+  // The type of trust store backing file to use.
+  private String trustStoreType;
+
+  // The certificate manager for the trust store.
+  private CertificateManager certificateManager;
+
+
+  /**
+   * Creates a new backend.  All backend
+   * implementations must implement a default constructor that use
+   * <CODE>super()</CODE> to invoke this constructor.
+   */
+  public TrustStoreBackend()
+  {
+    super();
+
+    // Perform all initialization in initializeBackend.
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void configureBackend(Configuration config) throws ConfigException
+  {
+    Validator.ensureNotNull(config);
+    Validator.ensureTrue(config instanceof TrustStoreBackendCfg);
+
+    configuration = (TrustStoreBackendCfg)config;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void initializeBackend()
+         throws ConfigException, InitializationException
+  {
+    // Create the set of base DNs that we will handle.  In this case, it's just
+    // the DN of the base trust store entry.
+    try
+    {
+      // FIXME -- Deal with this more correctly.
+      baseDN = DN.decode(DN_TRUST_STORE_ROOT);
+      this.baseDNs = new DN[] {baseDN};
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message =
+          ERR_TRUSTSTORE_CANNOT_DECODE_TRUSTSTORE_ROOT_DN.get(
+               getExceptionMessage(e));
+      throw new InitializationException(message, e);
+    }
+
+
+    DN configEntryDN = configuration.dn();
+
+    // Get the path to the trust store file.
+    trustStoreFile = configuration.getTrustStoreFile();
+
+
+    // Get the trust store type.  If none is specified, then use the default
+    // type.
+    trustStoreType = configuration.getTrustStoreType();
+    if (trustStoreType == null)
+    {
+      trustStoreType = KeyStore.getDefaultType();
+    }
+
+    try
+    {
+      KeyStore.getInstance(trustStoreType);
+    }
+    catch (KeyStoreException kse)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+      }
+
+      Message message = ERR_TRUSTSTORE_INVALID_TYPE.
+          get(String.valueOf(trustStoreType), String.valueOf(configEntryDN),
+              getExceptionMessage(kse));
+      throw new InitializationException(message);
+    }
+
+
+    // Get the PIN needed to access the contents of the trust store file.  We
+    // will offer several places to look for the PIN, and we will do so in the
+    // following order:
+    // - In a specified Java property
+    // - In a specified environment variable
+    // - In a specified file on the server filesystem.
+    // - As the value of a configuration attribute.
+    // In any case, the PIN must be in the clear.  If no PIN is provided, then
+    // it will be assumed that none is required to access the information in the
+    // trust store.
+    String pinProperty = configuration.getTrustStorePinProperty();
+    if (pinProperty == null)
+    {
+      String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
+      if (pinEnVar == null)
+      {
+        String pinFilePath = configuration.getTrustStorePinFile();
+        if (pinFilePath == null)
+        {
+          String pinStr = configuration.getTrustStorePin();
+          if (pinStr == null)
+          {
+            trustStorePIN = null;
+          }
+          else
+          {
+            trustStorePIN = pinStr.toCharArray();
+          }
+        }
+        else
+        {
+          File pinFile = getFileForPath(pinFilePath);
+          if (! pinFile.exists())
+          {
+            try
+            {
+              // Generate a PIN.
+              trustStorePIN = createKeystorePassword();
+
+              // Store the PIN in the pin file.
+              createPINFile(pinFile.getPath(), new String(trustStorePIN));
+            }
+            catch (Exception e)
+            {
+              Message message = ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+                   String.valueOf(pinFilePath), String.valueOf(configEntryDN));
+              throw new InitializationException(message);
+            }
+          }
+          else
+          {
+            String pinStr;
+
+            BufferedReader br = null;
+            try
+            {
+              br = new BufferedReader(new FileReader(pinFile));
+              pinStr = br.readLine();
+            }
+            catch (IOException ioe)
+            {
+              Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.
+                  get(String.valueOf(pinFilePath),
+                      String.valueOf(configEntryDN), getExceptionMessage(ioe));
+              throw new InitializationException(message, ioe);
+            }
+            finally
+            {
+              try
+              {
+                br.close();
+              } catch (Exception e) {
+                // ignore
+              }
+            }
+
+            if (pinStr == null)
+            {
+              Message message = ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+                  String.valueOf(pinFilePath), String.valueOf(configEntryDN));
+              throw new InitializationException(message);
+            }
+            else
+            {
+              trustStorePIN     = pinStr.toCharArray();
+            }
+          }
+        }
+      }
+      else
+      {
+        String pinStr = System.getenv(pinEnVar);
+        if (pinStr == null)
+        {
+          Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+              String.valueOf(pinProperty), String.valueOf(configEntryDN));
+          throw new InitializationException(message);
+        }
+        else
+        {
+          trustStorePIN = pinStr.toCharArray();
+        }
+      }
+    }
+    else
+    {
+      String pinStr = System.getProperty(pinProperty);
+      if (pinStr == null)
+      {
+        Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+            String.valueOf(pinProperty), String.valueOf(configEntryDN));
+        throw new InitializationException(message);
+      }
+      else
+      {
+        trustStorePIN = pinStr.toCharArray();
+      }
+    }
+
+    // Create a certificate manager.
+    certificateManager =
+         new CertificateManager(getFileForPath(trustStoreFile).getPath(),
+                                trustStoreType,
+                                new String(trustStorePIN));
+
+    // Construct the trust store base entry.
+    LinkedHashMap<ObjectClass,String> objectClasses =
+         new LinkedHashMap<ObjectClass,String>(2);
+    objectClasses.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+
+    ObjectClass branchOC =
+         DirectoryServer.getObjectClass("ds-cfg-branch", true);
+    objectClasses.put(branchOC, "ds-cfg-branch");
+
+    LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
+         new LinkedHashMap<AttributeType,List<Attribute>>(0);
+    LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
+         new LinkedHashMap<AttributeType,List<Attribute>>(1);
+
+    RDN rdn = baseDN.getRDN();
+    int numAVAs = rdn.getNumValues();
+    for (int i=0; i < numAVAs; i++)
+    {
+      LinkedHashSet<AttributeValue> valueSet =
+           new LinkedHashSet<AttributeValue>(1);
+      valueSet.add(rdn.getAttributeValue(i));
+
+      AttributeType attrType = rdn.getAttributeType(i);
+      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+      attrList.add(new Attribute(attrType, attrType.getNameOrOID(),
+                                 valueSet));
+
+      userAttrs.put(attrType, attrList);
+    }
+
+    baseEntry = new Entry(baseDN, objectClasses, userAttrs,
+                                opAttrs);
+
+
+    // Define an empty sets for the supported controls and features.
+    supportedControls = new HashSet<String>(0);
+    supportedFeatures = new HashSet<String>(0);
+
+
+    // Register this as a change listener.
+    configuration.addTrustStoreChangeListener(this);
+
+
+    // Register the trust store base as a private suffix.
+    try
+    {
+      DirectoryServer.registerBaseDN(baseDN, this, true, false);
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get(
+          String.valueOf(baseDN), String.valueOf(e));
+      throw new InitializationException(message, e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void finalizeBackend()
+  {
+    configuration.addTrustStoreChangeListener(this);
+
+    try
+    {
+      DirectoryServer.deregisterBaseDN(baseDN, false);
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public DN[] getBaseDNs()
+  {
+    return baseDNs;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public long getEntryCount()
+  {
+    int numEntries = 1;
+
+    try
+    {
+      String[] aliases = certificateManager.getCertificateAliases();
+      if (aliases != null)
+      {
+        numEntries += aliases.length;
+      }
+    }
+    catch (KeyStoreException e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+    }
+
+    return numEntries;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isLocal()
+  {
+    // For the purposes of this method, this is a local backend.
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public Entry getEntry(DN entryDN)
+         throws DirectoryException
+  {
+    // If the requested entry was null, then throw an exception.
+    if (entryDN == null)
+    {
+      Message message = ERR_TRUSTSTORE_GET_ENTRY_NULL.get();
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+                                   message);
+    }
+
+
+    // If the requested entry was the backend base entry, then retrieve it.
+    if (entryDN.equals(baseDN))
+    {
+      return baseEntry.duplicate(true);
+    }
+
+
+    // See if the requested entry was one level below the backend base entry.
+    // If so, then it must point to a trust store entry.
+    DN parentDN = entryDN.getParentDNInSuffix();
+    if (parentDN == null)
+    {
+      return null;
+    }
+    else if (parentDN.equals(baseDN))
+    {
+      try
+      {
+        return getCertEntry(entryDN);
+      }
+      catch (DirectoryException e)
+      {
+        return null;
+      }
+    }
+    else
+    {
+      return null;
+    }
+  }
+
+
+
+  /**
+   * Generates an entry for a certificate based on the provided DN.  The
+   * DN must contain an RDN component that specifies the alias of the
+   * certificate, and that certificate alias must exist in the key store.
+   *
+   * @param  entryDN  The DN of the certificate to retrieve.
+   *
+   * @return  The requested certificate entry.
+   *
+   * @throws  DirectoryException  If the specified alias does not exist, or if
+   *                              the DN does not specify any alias.
+   */
+  private Entry getCertEntry(DN entryDN)
+         throws DirectoryException
+  {
+    // Make sure that the DN specifies a certificate alias.
+    AttributeType t =
+         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
+    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
+    if (v == null)
+    {
+      Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.
+           get(String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+                                   baseDN, null);
+    }
+
+
+    ByteString certValue;
+    try
+    {
+      Certificate cert = certificateManager.getCertificate(v.getStringValue());
+      certValue = new ASN1OctetString(cert.getEncoded());
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_TRUSTSTORE_INVALID_CERTIFICATE.get(
+          String.valueOf(entryDN), e.getMessage());
+      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+    }
+
+
+    // Construct the certificate entry to return.
+    LinkedHashMap<ObjectClass,String> ocMap =
+        new LinkedHashMap<ObjectClass,String>(2);
+    ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
+
+    ObjectClass objectClass =
+         DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true);
+    ocMap.put(objectClass, OC_INSTANCE_KEY);
+
+    LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
+         new LinkedHashMap<AttributeType,List<Attribute>>(0);
+    LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
+         new LinkedHashMap<AttributeType,List<Attribute>>(3);
+
+    LinkedHashSet<AttributeValue> valueSet =
+         new LinkedHashSet<AttributeValue>(1);
+    valueSet.add(v);
+
+    ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
+    attrList.add(new Attribute(t, t.getNameOrOID(), valueSet));
+    userAttrs.put(t, attrList);
+
+
+    t = DirectoryServer.getAttributeType(ATTR_ADS_CERTIFICATE, true);
+    valueSet = new LinkedHashSet<AttributeValue>(1);
+    valueSet.add(new AttributeValue(t,
+                          certValue));
+    attrList = new ArrayList<Attribute>(1);
+    LinkedHashSet<String> options = new LinkedHashSet<String>(1);
+    options.add("binary");
+    attrList.add(new Attribute(t, t.getNameOrOID(), options, valueSet));
+    userAttrs.put(t, attrList);
+
+
+    Entry e = new Entry(entryDN, ocMap, userAttrs, opAttrs);
+    e.processVirtualAttributes();
+    return e;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void addEntry(Entry entry, AddOperation addOperation)
+         throws DirectoryException
+  {
+    DN entryDN = entry.getDN();
+
+    if (entryDN.equals(baseDN))
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS, message);
+    }
+
+    DN parentDN = entryDN.getParentDNInSuffix();
+    if (parentDN == null)
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+    }
+
+    if (parentDN.equals(baseDN))
+    {
+      addCertificate(entry);
+    }
+    else
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+    }
+
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void deleteEntry(DN entryDN, DeleteOperation deleteOperation)
+         throws DirectoryException
+  {
+    Message message = ERR_TRUSTSTORE_DELETE_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void replaceEntry(Entry entry, ModifyOperation modifyOperation)
+         throws DirectoryException
+  {
+    Message message = ERR_TRUSTSTORE_MODIFY_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void renameEntry(DN currentDN, Entry entry,
+                                   ModifyDNOperation modifyDNOperation)
+         throws DirectoryException
+  {
+    Message message = ERR_TRUSTSTORE_MODIFY_DN_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void search(SearchOperation searchOperation)
+         throws DirectoryException
+  {
+    // Get the base entry for the search, if possible.  If it doesn't exist,
+    // then this will throw an exception.
+    DN    baseDN    = searchOperation.getBaseDN();
+    Entry baseEntry = getEntry(baseDN);
+
+
+    // Look at the base DN and see if it's the trust store base DN, or a
+    // trust store entry DN.
+    SearchScope  scope  = searchOperation.getScope();
+    SearchFilter filter = searchOperation.getFilter();
+    if (this.baseDN.equals(baseDN))
+    {
+      if ((scope == SearchScope.BASE_OBJECT) ||
+          (scope == SearchScope.WHOLE_SUBTREE))
+      {
+        if (filter.matchesEntry(baseEntry))
+        {
+          searchOperation.returnEntry(baseEntry, null);
+        }
+      }
+
+      String[] aliases = null;
+      try
+      {
+        aliases = certificateManager.getCertificateAliases();
+      }
+      catch (KeyStoreException e)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, e);
+        }
+      }
+
+      if (aliases == null)
+      {
+        aliases = new String[0];
+      }
+
+      if ((scope != SearchScope.BASE_OBJECT) && (! (aliases.length == 0) ))
+      {
+        AttributeType certAliasType =
+             DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
+        for (String alias : aliases)
+        {
+          DN certDN = makeChildDN(this.baseDN, certAliasType,
+                                  alias);
+
+          Entry certEntry;
+          try
+          {
+            certEntry = getCertEntry(certDN);
+          }
+          catch (Exception e)
+          {
+            if (debugEnabled())
+            {
+              TRACER.debugCaught(DebugLogLevel.ERROR, e);
+            }
+
+            continue;
+          }
+
+          if (filter.matchesEntry(certEntry))
+          {
+            searchOperation.returnEntry(certEntry, null);
+          }
+
+        }
+      }
+    }
+    else if (this.baseDN.equals(baseDN.getParentDNInSuffix()))
+    {
+      Entry certEntry = getCertEntry(baseDN);
+
+      if ((scope == SearchScope.BASE_OBJECT) ||
+          (scope == SearchScope.WHOLE_SUBTREE))
+      {
+        if (filter.matchesEntry(certEntry))
+        {
+          searchOperation.returnEntry(certEntry, null);
+        }
+      }
+    }
+    else
+    {
+      Message message = ERR_TRUSTSTORE_INVALID_BASE.get(String.valueOf(baseDN));
+      throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public HashSet<String> getSupportedControls()
+  {
+    return supportedControls;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public HashSet<String> getSupportedFeatures()
+  {
+    return supportedFeatures;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean supportsLDIFExport()
+  {
+    // We do not support LDIF exports.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void exportLDIF(LDIFExportConfig exportConfig)
+         throws DirectoryException
+  {
+    Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean supportsLDIFImport()
+  {
+    // This backend does not support LDIF imports.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public LDIFImportResult importLDIF(LDIFImportConfig importConfig)
+         throws DirectoryException
+  {
+    // This backend does not support LDIF imports.
+    Message message = ERR_TRUSTSTORE_IMPORT_AND_EXPORT_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean supportsBackup()
+  {
+    // This backend does not provide a backup/restore mechanism.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean supportsBackup(BackupConfig backupConfig,
+                                StringBuilder unsupportedReason)
+  {
+    // This backend does not provide a backup/restore mechanism.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void createBackup(BackupConfig backupConfig)
+  throws DirectoryException
+  {
+    // This backend does not provide a backup/restore mechanism.
+    Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void removeBackup(BackupDirectory backupDirectory,
+                           String backupID)
+         throws DirectoryException
+  {
+    // This backend does not provide a backup/restore mechanism.
+    Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean supportsRestore()
+  {
+    // This backend does not provide a backup/restore mechanism.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public void restoreBackup(RestoreConfig restoreConfig)
+         throws DirectoryException
+  {
+    // This backend does not provide a backup/restore mechanism.
+    Message message = ERR_TRUSTSTORE_BACKUP_AND_RESTORE_NOT_SUPPORTED.get();
+    throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException
+  {
+    return ConditionResult.UNDEFINED;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public long numSubordinates(DN entryDN) throws DirectoryException
+  {
+    return -1;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isConfigurationChangeAcceptable(
+       TrustStoreBackendCfg configuration, List<Message> unacceptableReasons)
+  {
+    boolean configAcceptable = true;
+    DN cfgEntryDN = configuration.dn();
+
+
+    // Get the path to the trust store file.
+    String newTrustStoreFile = configuration.getTrustStoreFile();
+    try
+    {
+      File f = getFileForPath(newTrustStoreFile);
+      if (!(f.exists() && f.isFile()))
+      {
+        unacceptableReasons.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
+                String.valueOf(newTrustStoreFile),
+                String.valueOf(cfgEntryDN)));
+        configAcceptable = false;
+      }
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      unacceptableReasons.add(ERR_TRUSTSTORE_CANNOT_DETERMINE_FILE.get(
+              String.valueOf(cfgEntryDN),
+              getExceptionMessage(e)));
+      configAcceptable = false;
+    }
+
+
+    // Check to see if the trust store type is acceptable.
+    String storeType = configuration.getTrustStoreType();
+    if (storeType != null)
+    {
+      try
+      {
+        KeyStore.getInstance(storeType);
+      }
+      catch (KeyStoreException kse)
+      {
+        if (debugEnabled())
+        {
+          TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+        }
+
+        Message message = ERR_TRUSTSTORE_INVALID_TYPE.get(
+                String.valueOf(storeType),
+                String.valueOf(cfgEntryDN),
+                getExceptionMessage(kse));
+        unacceptableReasons.add(message);
+        configAcceptable = false;
+      }
+    }
+
+
+    // If there is a PIN property, then make sure the corresponding
+    // property is set.
+    String pinProp = configuration.getTrustStorePinProperty();
+    if (pinProp != null)
+    {
+      if (System.getProperty(pinProp) == null)
+      {
+        Message message = ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+                String.valueOf(pinProp),
+                String.valueOf(cfgEntryDN));
+        unacceptableReasons.add(message);
+        configAcceptable = false;
+      }
+    }
+
+
+    // If there is a PIN environment variable, then make sure the corresponding
+    // environment variable is set.
+    String pinEnVar = configuration.getTrustStorePinEnvironmentVariable();
+    if (pinEnVar != null)
+    {
+      if (System.getenv(pinEnVar) == null)
+      {
+        Message message = ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+                String.valueOf(pinEnVar),
+                String.valueOf(cfgEntryDN));
+        unacceptableReasons.add(message);
+        configAcceptable = false;
+      }
+    }
+
+
+    // If there is a PIN file, then make sure the file is readable if it exists.
+    String pinFile = configuration.getTrustStorePinFile();
+    if (pinFile != null)
+    {
+      File f = new File(pinFile);
+      if (f.exists())
+      {
+        String pinStr = null;
+
+        BufferedReader br = null;
+        try
+        {
+          br = new BufferedReader(new FileReader(pinFile));
+          pinStr = br.readLine();
+        }
+        catch (IOException ioe)
+        {
+          Message message = ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
+                  String.valueOf(pinFile),
+                  String.valueOf(cfgEntryDN),
+                  getExceptionMessage(ioe));
+          unacceptableReasons.add(message);
+          configAcceptable = false;
+        }
+        finally
+        {
+          try
+          {
+            br.close();
+          } catch (Exception e) {
+            // ignore
+          }
+        }
+
+        if (pinStr == null)
+        {
+          Message message =  ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+                  String.valueOf(pinFile),
+                  String.valueOf(cfgEntryDN));
+          unacceptableReasons.add(message);
+          configAcceptable = false;
+        }
+      }
+    }
+
+
+    return configAcceptable;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public ConfigChangeResult applyConfigurationChange(TrustStoreBackendCfg cfg)
+  {
+    ResultCode        resultCode          = ResultCode.SUCCESS;
+    boolean           adminActionRequired = false;
+    ArrayList<Message> messages            = new ArrayList<Message>();
+    DN configEntryDN = cfg.dn();
+
+    // Get the path to the trust store file.
+    String newTrustStoreFile = cfg.getTrustStoreFile();
+    File f = getFileForPath(newTrustStoreFile);
+    if (! (f.exists() && f.isFile()))
+    {
+      resultCode = DirectoryServer.getServerErrorResultCode();
+
+      messages.add(ERR_TRUSTSTORE_NO_SUCH_FILE.get(
+              String.valueOf(newTrustStoreFile),
+              String.valueOf(configEntryDN)));
+    }
+
+
+    // Get the trust store type.  If none is specified, then use the default
+    // type.
+    String newTrustStoreType = cfg.getTrustStoreType();
+    if (newTrustStoreType == null)
+    {
+      newTrustStoreType = KeyStore.getDefaultType();
+    }
+
+    try
+    {
+      KeyStore.getInstance(newTrustStoreType);
+    }
+    catch (KeyStoreException kse)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, kse);
+      }
+
+      messages.add(ERR_TRUSTSTORE_INVALID_TYPE.get(
+              String.valueOf(newTrustStoreType),
+              String.valueOf(configEntryDN),
+              getExceptionMessage(kse)));
+
+      resultCode = DirectoryServer.getServerErrorResultCode();
+    }
+
+
+    // Get the PIN needed to access the contents of the trust store file.  We
+    // will offer several places to look for the PIN, and we will do so in the
+    // following order:
+    // - In a specified Java property
+    // - In a specified environment variable
+    // - In a specified file on the server filesystem.
+    // - As the value of a configuration attribute.
+    // In any case, the PIN must be in the clear.  If no PIN is provided, then
+    // it will be assumed that none is required to access the information in the
+    // trust store.
+    char[] newPIN = null;
+    String newPINProperty = cfg.getTrustStorePinProperty();
+    if (newPINProperty == null)
+    {
+      String newPINEnVar = cfg.getTrustStorePinEnvironmentVariable();
+      if (newPINEnVar == null)
+      {
+        String newPINFile = cfg.getTrustStorePinFile();
+        if (newPINFile == null)
+        {
+          String pinStr = cfg.getTrustStorePin();
+          if (pinStr == null)
+          {
+            newPIN = null;
+          }
+          else
+          {
+            newPIN = pinStr.toCharArray();
+          }
+        }
+        else
+        {
+          File pinFile = getFileForPath(newPINFile);
+          if (! pinFile.exists())
+          {
+            try
+            {
+              // Generate a PIN.
+              newPIN = createKeystorePassword();
+
+              // Store the PIN in the pin file.
+              createPINFile(pinFile.getPath(), new String(newPIN));
+            }
+            catch (Exception e)
+            {
+              resultCode = DirectoryServer.getServerErrorResultCode();
+
+              messages.add(ERR_TRUSTSTORE_PIN_NO_SUCH_FILE.get(
+                      String.valueOf(newPINFile),
+                      String.valueOf(configEntryDN)));
+            }
+          }
+          else
+          {
+            String pinStr = null;
+
+            BufferedReader br = null;
+            try
+            {
+              br = new BufferedReader(new FileReader(pinFile));
+              pinStr = br.readLine();
+            }
+            catch (IOException ioe)
+            {
+              resultCode = DirectoryServer.getServerErrorResultCode();
+
+              messages.add(ERR_TRUSTSTORE_PIN_FILE_CANNOT_READ.get(
+                      String.valueOf(newPINFile),
+                      String.valueOf(configEntryDN),
+                      getExceptionMessage(ioe)));
+            }
+            finally
+            {
+              try
+              {
+                br.close();
+              } catch (Exception e) {
+                // ignore
+              }
+            }
+
+            if (pinStr == null)
+            {
+              resultCode = DirectoryServer.getServerErrorResultCode();
+
+              messages.add(ERR_TRUSTSTORE_PIN_FILE_EMPTY.get(
+                      String.valueOf(newPINFile),
+                      String.valueOf(configEntryDN)));
+            }
+            else
+            {
+              newPIN = pinStr.toCharArray();
+            }
+          }
+        }
+      }
+      else
+      {
+        String pinStr = System.getenv(newPINEnVar);
+        if (pinStr == null)
+        {
+          resultCode = DirectoryServer.getServerErrorResultCode();
+
+          messages.add(ERR_TRUSTSTORE_PIN_ENVAR_NOT_SET.get(
+                  String.valueOf(newPINEnVar),
+                  String.valueOf(configEntryDN)));
+        }
+        else
+        {
+          newPIN = pinStr.toCharArray();
+        }
+      }
+    }
+    else
+    {
+      String pinStr = System.getProperty(newPINProperty);
+      if (pinStr == null)
+      {
+        resultCode = DirectoryServer.getServerErrorResultCode();
+
+        messages.add(ERR_TRUSTSTORE_PIN_PROPERTY_NOT_SET.get(
+                String.valueOf(newPINProperty),
+                String.valueOf(configEntryDN)));
+      }
+      else
+      {
+        newPIN = pinStr.toCharArray();
+      }
+    }
+
+
+    if (resultCode == ResultCode.SUCCESS)
+    {
+      trustStoreFile = newTrustStoreFile;
+      trustStoreType = newTrustStoreType;
+      trustStorePIN  = newPIN;
+      configuration  = cfg;
+      certificateManager =
+           new CertificateManager(getFileForPath(trustStoreFile).getPath(),
+                                  trustStoreType,
+                                  new String(trustStorePIN));
+    }
+
+
+    return new ConfigChangeResult(resultCode, adminActionRequired, messages);
+  }
+
+  /**
+   * Create a new child DN from a given parent DN.  The child RDN is formed
+   * from a given attribute type and string value.
+   * @param parentDN The DN of the parent.
+   * @param rdnAttrType The attribute type of the RDN.
+   * @param rdnStringValue The string value of the RDN.
+   * @return A new child DN.
+   */
+  public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType,
+                               String rdnStringValue)
+  {
+    AttributeValue attrValue =
+         new AttributeValue(rdnAttrType, rdnStringValue);
+    return parentDN.concat(RDN.create(rdnAttrType, attrValue));
+  }
+
+
+  /**
+   * Retrieves a set of <CODE>KeyManager</CODE> objects that may be used for
+   * interactions requiring access to a key manager.
+   *
+   * @return  A set of <CODE>KeyManager</CODE> objects that may be used for
+   *          interactions requiring access to a key manager.
+   *
+   * @throws DirectoryException  If a problem occurs while attempting to obtain
+   *                             the set of key managers.
+   */
+  public KeyManager[] getKeyManagers()
+         throws DirectoryException
+  {
+    KeyStore keyStore;
+    try
+    {
+      keyStore = KeyStore.getInstance(trustStoreType);
+
+      FileInputStream inputStream =
+           new FileInputStream(getFileForPath(trustStoreFile));
+      keyStore.load(inputStream, trustStorePIN);
+      inputStream.close();
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_TRUSTSTORE_CANNOT_LOAD.get(
+          trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+                                   message, e);
+    }
+
+
+    try
+    {
+      String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+      KeyManagerFactory keyManagerFactory =
+           KeyManagerFactory.getInstance(keyManagerAlgorithm);
+      keyManagerFactory.init(keyStore, trustStorePIN);
+      return keyManagerFactory.getKeyManagers();
+    }
+    catch (Exception e)
+    {
+      if (debugEnabled())
+      {
+        TRACER.debugCaught(DebugLogLevel.ERROR, e);
+      }
+
+      Message message = ERR_TRUSTSTORE_CANNOT_CREATE_FACTORY.get(
+          trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
+                                   message, e);
+    }
+  }
+
+
+  /**
+   * Retrieves a set of {@code TrustManager} objects that may be used
+   * for interactions requiring access to a trust manager.
+   *
+   * @return  A set of {@code TrustManager} objects that may be used
+   *          for interactions requiring access to a trust manager.
+   *
+   * @throws  DirectoryException  If a problem occurs while attempting
+   *                              to obtain the set of trust managers.
+   */
+  public TrustManager[] getTrustManagers()
+         throws DirectoryException
+  {
+    // TODO Temporary until the trust store is populated with the certificates
+    // TODO   of all the servers in the ADS topology.
+    TrustManagerProvider trustManagerProvider = new BlindTrustManagerProvider();
+    return trustManagerProvider.getTrustManagers();
+  }
+
+
+  private void addCertificate(Entry entry)
+       throws DirectoryException
+  {
+    DN entryDN = entry.getDN();
+
+    // Make sure that the DN specifies a certificate alias.
+    AttributeType t =
+         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
+    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
+    if (v == null)
+    {
+      Message message = ERR_TRUSTSTORE_DN_DOES_NOT_SPECIFY_CERTIFICATE.get(
+           String.valueOf(entryDN));
+      throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message,
+                                   baseDN, null);
+    }
+    String certAlias = v.getStringValue();
+
+    try
+    {
+      if (certificateManager.aliasInUse(v.getStringValue()))
+      {
+        Message message = ERR_TRUSTSTORE_ALIAS_IN_USE.get(
+             String.valueOf(entryDN));
+        throw new DirectoryException(ResultCode.ENTRY_ALREADY_EXISTS,
+                                     message);
+      }
+
+      ObjectClass ocSelfSignedCertRequest =
+           DirectoryServer.getObjectClass(OC_SELF_SIGNED_CERT_REQUEST, true);
+      if (entry.hasObjectClass(ocSelfSignedCertRequest))
+      {
+        try
+        {
+          certificateManager.generateSelfSignedCertificate(
+             certAlias,
+             getADSCertificateSubjectDN(),
+             getADSCertificateValidity());
+        }
+        catch (Exception e)
+        {
+          Message message = ERR_TRUSTSTORE_CANNOT_GENERATE_CERT.get(
+              certAlias, trustStoreFile, getExceptionMessage(e));
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message, e);
+        }
+      }
+      else
+      {
+        List<Attribute> certAttrs = entry.getAttribute(ATTR_ADS_CERTIFICATE);
+        if (certAttrs == null)
+        {
+          Message message =
+               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get(
+                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message);
+        }
+        if (certAttrs.size() != 1)
+        {
+          Message message =
+               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get(
+                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message);
+        }
+
+        LinkedHashSet<AttributeValue> certValues = certAttrs.get(0).getValues();
+        if (certValues == null)
+        {
+          Message message =
+               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get(
+                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message);
+        }
+        if (certValues.size() != 1)
+        {
+          Message message =
+               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get(
+                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message);
+        }
+
+        byte[] certBytes = certValues.iterator().next().getValueBytes();
+        try
+        {
+          File tempDir = getFileForPath("config");
+          File tempFile = File.createTempFile(configuration.getBackendId(),
+                                              certAlias, tempDir);
+          try
+          {
+            FileOutputStream outputStream =
+                 new FileOutputStream(tempFile.getPath(), false);
+            try
+            {
+              outputStream.write(certBytes);
+            }
+            finally
+            {
+              outputStream.close();
+            }
+
+            certificateManager.addCertificate(certAlias, tempFile);
+          }
+          finally
+          {
+            tempFile.delete();
+          }
+        }
+        catch (IOException e)
+        {
+          Message message = ERR_TRUSTSTORE_CANNOT_WRITE_CERT.get(
+              certAlias, getExceptionMessage(e));
+          throw new DirectoryException(
+               DirectoryServer.getServerErrorResultCode(), message, e);
+        }
+      }
+    }
+    catch (Exception e)
+    {
+      Message message = ERR_TRUSTSTORE_CANNOT_ADD_CERT.get(
+           certAlias, trustStoreFile, getExceptionMessage(e));
+      throw new DirectoryException(
+           DirectoryServer.getServerErrorResultCode(), message, e);
+    }
+
+  }
+
+
+  /**
+   * Returns the validity period to be used to generate the ADS certificate.
+   * @return The validity period to be used to generate the ADS certificate.
+   */
+  private static int getADSCertificateValidity()
+  {
+    return 20 * 365;
+  }
+
+  /**
+   * Returns the Subject DN to be used to generate the ADS certificate.
+   * @return The Subject DN to be used to generate the ADS certificate.
+   * @throws java.net.UnknownHostException If the server host name could not be
+   *                                       determined.
+   */
+  private static String getADSCertificateSubjectDN()
+       throws UnknownHostException
+  {
+    String hostname =
+         java.net.InetAddress.getLocalHost().getCanonicalHostName();
+    return "cn=" + Rdn.escapeValue(hostname) + ",O=OpenDS Certificate";
+  }
+
+  /**
+   * Create a randomly generated password for a certificate keystore.
+   * @return A randomly generated password for a certificate keystore.
+   */
+  private static char[] createKeystorePassword() {
+    int pwdLength = 50;
+    char[] pwd = new char[pwdLength];
+    Random random = new Random();
+    for (int pos=0; pos < pwdLength; pos++) {
+        int type = getRandomInt(random,3);
+        char nextChar = getRandomChar(random,type);
+        pwd[pos] = nextChar;
+    }
+    return pwd;
+  }
+
+  private static char getRandomChar(Random random, int type)
+  {
+    char generatedChar;
+    int next = random.nextInt();
+    int d;
+
+    switch (type)
+    {
+    case 0:
+      // Will return a digit
+      d = next % 10;
+      if (d < 0)
+      {
+        d = d * (-1);
+      }
+      generatedChar = (char) (d+48);
+      break;
+    case 1:
+      // Will return a lower case letter
+      d = next % 26;
+      if (d < 0)
+      {
+        d = d * (-1);
+      }
+      generatedChar =  (char) (d + 97);
+      break;
+    default:
+      // Will return a capital letter
+      d = (next % 26);
+      if (d < 0)
+      {
+        d = d * (-1);
+      }
+      generatedChar = (char) (d + 65) ;
+    }
+
+    return generatedChar;
+  }
+
+  private static int getRandomInt(Random random,int modulo)
+  {
+    return (random.nextInt() & modulo);
+  }
+
+  /**
+   * Creates a PIN file on the specified path.
+   * @param path the path where the PIN file will be created.
+   * @param pin The PIN to store in the file.
+   * @throws IOException if something goes wrong.
+   */
+  public static void createPINFile(String path, String pin)
+       throws IOException
+  {
+    FileWriter file = new FileWriter(path);
+    PrintWriter out = new PrintWriter(file);
+
+    out.println(pin);
+
+    out.flush();
+    out.close();
+
+    if(FilePermission.canSetPermissions()) {
+      try {
+        if (!FilePermission.setPermissions(new File(path),
+                                           new FilePermission(0600)))
+        {
+          // Log a warning that the permissions were not set.
+          Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
+          ErrorLogger.logError(message);
+        }
+      } catch(DirectoryException e) {
+        // Log a warning that the permissions were not set.
+        Message message = WARN_TRUSTSTORE_SET_PERMISSIONS_FAILED.get(path);
+        ErrorLogger.logError(message);
+      }
+    }
+  }
+
+}
+

--
Gitblit v1.10.0