mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

coulbeck
28.04.2007 59b14574946fe571a7419aa36ff781e535765375
Files missing from previous commit
3 files added
1879 ■■■■■ changed files
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml 145 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml 115 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java 1619 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/CryptoManagerConfiguration.xml
New file
@@ -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>
opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/TrustStoreBackendConfiguration.xml
New file
@@ -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>
opendj-sdk/opends/src/server/org/opends/server/backends/TrustStoreBackend.java
New file
@@ -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);
      }
    }
  }
}