From 4daff1f3fd3f8e9e4f0135eee76fa1ca2ce02e4e Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Wed, 28 Feb 2007 21:01:32 +0000
Subject: [PATCH] Add a new key manager which provides the ability to specify which certificate should be presented based on its alias (aka its nickname).  This is used both by server-side code which needs to present a certificate to clients, as well as by client-side code which needs to present a certificate to the server.

---
 opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java                                            |   18 +
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java                                                |   20 +
 opendj-sdk/opends/src/server/org/opends/server/util/SelectableCertificateKeyManager.java                            |  315 +++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java                              |    4 
 opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/RmiConnector.java                                      |   22 +
 opendj-sdk/opends/src/server/org/opends/server/tools/StopDS.java                                                    |   20 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java |   17 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java                |  110 +++++++
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java               |  112 +++++++
 opendj-sdk/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java                        |   16 
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPModify.java                                                |   22 +
 opendj-sdk/opends/src/server/org/opends/server/tools/SSLConnectionFactory.java                                      |   10 
 opendj-sdk/opends/resource/config/config.ldif                                                                       |    2 
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPCompare.java                                               |   20 +
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPDelete.java                                                |   22 +
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java                |  110 +++++++
 opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java                            |   18 +
 opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java                                           |   13 
 opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java                                        |    2 
 opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java                             |   17 +
 20 files changed, 859 insertions(+), 31 deletions(-)

diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index c8e3e55..58f472b 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -374,7 +374,7 @@
 ds-cfg-connection-handler-enabled: true
 ds-cfg-use-ssl: false
 ds-cfg-listen-port: 1689
-ds-cfg-ssl-cert-nickname: adm-server-cert
+ds-cfg-ssl-cert-nickname: server-cert
 
 dn: cn=Entry Cache,cn=config
 objectClass: top
diff --git a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
index 4eaa9b8..a3a4697 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/api/ClientConnection.java
@@ -1488,6 +1488,24 @@
 
 
   /**
+   * Retrieves the alias of the server certificate that should be used
+   * for operations requiring a server certificate.  The default
+   * implementation returns {@code null} to indicate that any alias is
+   * acceptable.
+   *
+   * @return  The alias of the server certificate that should be used
+   *          for operations requring a server certificate, or
+   *          {@code null} if any alias is acceptable.
+   */
+  public String getCertificateAlias()
+  {
+    // In the default implementation, we'll return null.
+    return null;
+  }
+
+
+
+  /**
    * Retrieves a string representation of this client connection.
    *
    * @return  A string representation of this client connection.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java b/opendj-sdk/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
index 292dab1..4dc1a09 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/extensions/TLSConnectionSecurityProvider.java
@@ -50,6 +50,7 @@
 import org.opends.server.types.DisconnectReason;
 import org.opends.server.types.InitializationException;
 import org.opends.server.types.SSLClientAuthPolicy;
+import org.opends.server.util.SelectableCertificateKeyManager;
 
 import static org.opends.server.loggers.Debug.*;
 import static org.opends.server.messages.ExtensionsMessages.*;
@@ -195,8 +196,19 @@
     {
       // FIXME -- Is it bad to create a new SSLContext for each connection?
       sslContext = SSLContext.getInstance(SSL_CONTEXT_INSTANCE_NAME);
-      sslContext.init(keyManagerProvider.getKeyManagers(),
-                      trustManagerProvider.getTrustManagers(), null);
+
+      String alias = clientConnection.getCertificateAlias();
+      if (alias == null)
+      {
+        sslContext.init(keyManagerProvider.getKeyManagers(),
+                        trustManagerProvider.getTrustManagers(), null);
+      }
+      else
+      {
+        sslContext.init(SelectableCertificateKeyManager.wrap(
+                             keyManagerProvider.getKeyManagers(), alias),
+                        trustManagerProvider.getTrustManagers(), null);
+      }
     }
     catch (Exception e)
     {
diff --git a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
index ae6eefb..978c092 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/messages/ToolMessages.java
@@ -7655,6 +7655,17 @@
   public static final int MSGID_STOPDS_CHECK_STOPPABILITY =
       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 804;
 
+
+
+  /**
+   * The message ID for the message that will be used as the description of the
+   * certNickname argument.  This does not take any arguments.
+   */
+  public static final int MSGID_DESCRIPTION_CERT_NICKNAME =
+       CATEGORY_MASK_TOOLS | SEVERITY_MASK_INFORMATIONAL | 805;
+
+
+
   /**
    * Associates a set of generic messages with the message IDs defined in this
    * class.
@@ -8117,6 +8128,8 @@
                     "Certificate keystore PIN");
     registerMessage(MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE,
                     "Certificate keystore PIN file");
+    registerMessage(MSGID_DESCRIPTION_CERT_NICKNAME,
+                    "Nickname of certificate for SSL client authentication");
     registerMessage(MSGID_DESCRIPTION_TRUSTSTOREPASSWORD,
                     "Certificate trust store PIN");
     registerMessage(MSGID_DESCRIPTION_TRUSTSTOREPASSWORD_FILE,
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
index b2bb2ab..4240962 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
@@ -1062,9 +1062,9 @@
       if (certNameAttr == null)
       {
         //
-        // This is fine -- we'll just use the default.
+        // This is fine -- we'll just let the server pick one.
         certNameAttr = new StringConfigAttribute(ATTR_SSL_CERT_NICKNAME,
-            getMessage(msgID), false, false, false, DEFAULT_SSL_CERT_NICKNAME);
+            getMessage(msgID), false, false, false, (String) null);
       }
       return certNameAttr;
     }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/RmiConnector.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/RmiConnector.java
index cc4c9b4..2742394 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/RmiConnector.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/jmx/RmiConnector.java
@@ -32,6 +32,7 @@
 import java.rmi.registry.Registry;
 import java.util.HashMap;
 
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLSocketFactory;
 import javax.net.ssl.SSLContext;
 
@@ -48,6 +49,8 @@
 import org.opends.server.types.DebugLogCategory;
 import org.opends.server.types.DebugLogSeverity;
 
+import org.opends.server.util.SelectableCertificateKeyManager;
+
 import static org.opends.server.loggers.Debug.*;
 
 /**
@@ -320,15 +323,30 @@
         // ---------------------
         //
         // Get a Server socket factory
+        KeyManager[] keyManagers;
         KeyManagerProvider provider = jmxConnectionHandler.keyManagerProvider;
         if (provider == null)
         {
-          provider = new NullKeyManagerProvider();
+          keyManagers = new NullKeyManagerProvider().getKeyManagers();
+        }
+        else
+        {
+          String nickname = jmxConnectionHandler.sslServerCertNickname;
+          if (nickname == null)
+          {
+            keyManagers = provider.getKeyManagers();
+          }
+          else
+          {
+            keyManagers =
+                 SelectableCertificateKeyManager.wrap(provider.getKeyManagers(),
+                                                      nickname);
+          }
         }
 
         SSLContext ctx = SSLContext.getInstance("TLSv1");
         ctx.init(
-            provider.getKeyManagers(),
+            keyManagers,
             null,
             null);
         SSLSocketFactory ssf = ctx.getSocketFactory();
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
index 06bad10..4cd3342 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPClientConnection.java
@@ -2653,5 +2653,22 @@
 
     return connectionHandler.getTrustManagerProviderDN();
   }
+
+
+
+  /**
+   * Retrieves the alias of the server certificate that should be used
+   * for operations requiring a server certificate.  The default
+   * implementation returns {@code null} to indicate that any alias is
+   * acceptable.
+   *
+   * @return  The alias of the server certificate that should be used
+   *          for operations requring a server certificate, or
+   *          {@code null} if any alias is acceptable.
+   */
+  public String getCertificateAlias()
+  {
+    return connectionHandler.getSSLServerCertNickname();
+  }
 }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
index 4253411..dd0c2ef 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/ldap/LDAPConnectionHandler.java
@@ -901,8 +901,8 @@
            configEntry.getConfigAttribute(certNameStub);
       if (certNameAttr == null)
       {
-        // This is fine -- we'll just use the default.
-        sslServerCertNickname = DEFAULT_SSL_CERT_NICKNAME;
+        // This is fine -- We'll just let the server pick one.
+        sslServerCertNickname = null;
       }
       else
       {
@@ -1792,6 +1792,20 @@
 
 
   /**
+   * Retrieves the nickname of the server certificate that should be used in
+   * conjunction with this LDAP connection handler.
+   *
+   * @return  The nickname of the server certificate that should be used in
+   *          conjunction with this LDAP connection handler.
+   */
+  public String getSSLServerCertNickname()
+  {
+    return sslServerCertNickname;
+  }
+
+
+
+  /**
    * Retrieves the maximum ASN.1 element value length that will be allowed by
    * this connection handler.
    *
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPCompare.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPCompare.java
index 6a099c6..ba5d156 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPCompare.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPCompare.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -343,6 +343,7 @@
     StringArgument    assertionFilter        = null;
     StringArgument    bindDN                 = null;
     StringArgument    bindPassword           = null;
+    StringArgument    certNickname           = null;
     StringArgument    controlStr             = null;
     StringArgument    encodingStr            = null;
     StringArgument    filename               = null;
@@ -438,6 +439,11 @@
                                  MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE);
       argParser.addArgument(keyStorePasswordFile);
 
+      certNickname = new StringArgument("certnickname", 'N', "certNickname",
+                                        false, false, true, "{nickname}", null,
+                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
+      argParser.addArgument(certNickname);
+
       trustStorePath = new StringArgument("trustStorePath", 'P',
                                           "trustStorePath", false, false, true,
                                           "{trustStorePath}", null, null,
@@ -796,9 +802,19 @@
       SSLConnectionFactory sslConnectionFactory = null;
       if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
       {
+        String clientAlias;
+        if (certNickname.isPresent())
+        {
+          clientAlias = certNickname.getValue();
+        }
+        else
+        {
+          clientAlias = null;
+        }
+
         sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
-                                  keyStorePasswordValue,
+                                  keyStorePasswordValue, clientAlias,
                                   trustStorePathValue, trustStorePasswordValue);
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPDelete.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPDelete.java
index 1e9db57..9b6a5c5 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPDelete.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPDelete.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -323,6 +323,7 @@
     IntegerArgument   version                = null;
     StringArgument    bindDN                 = null;
     StringArgument    bindPassword           = null;
+    StringArgument    certNickname           = null;
     StringArgument    controlStr             = null;
     StringArgument    encodingStr            = null;
     StringArgument    filename               = null;
@@ -415,6 +416,11 @@
                                  MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE);
       argParser.addArgument(keyStorePasswordFile);
 
+      certNickname = new StringArgument("certnickname", 'N', "certNickname",
+                                        false, false, true, "{nickname}", null,
+                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
+      argParser.addArgument(certNickname);
+
       trustStorePath = new StringArgument("trustStorePath", 'P',
                                           "trustStorePath", false, false, true,
                                           "{trustStorePath}", null, null,
@@ -679,10 +685,20 @@
       SSLConnectionFactory sslConnectionFactory = null;
       if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
       {
+        String clientAlias;
+        if (certNickname.isPresent())
+        {
+          clientAlias = certNickname.getValue();
+        }
+        else
+        {
+          clientAlias = null;
+        }
+
         sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
-                                  keyStorePasswordValue, trustStorePathValue,
-                                  trustStorePasswordValue);
+                                  keyStorePasswordValue, clientAlias,
+                                  trustStorePathValue, trustStorePasswordValue);
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPModify.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPModify.java
index 65cc23a..38e0831 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPModify.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPModify.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -581,6 +581,7 @@
     StringArgument    assertionFilter        = null;
     StringArgument    bindDN                 = null;
     StringArgument    bindPassword           = null;
+    StringArgument    certNickname           = null;
     StringArgument    controlStr             = null;
     StringArgument    encodingStr            = null;
     StringArgument    filename               = null;
@@ -677,6 +678,11 @@
                                  MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE);
       argParser.addArgument(keyStorePasswordFile);
 
+      certNickname = new StringArgument("certnickname", 'N', "certNickname",
+                                        false, false, true, "{nickname}", null,
+                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
+      argParser.addArgument(certNickname);
+
       trustStorePath = new StringArgument("trustStorePath", 'P',
                                           "trustStorePath", false, false, true,
                                           "{trustStorePath}", null, null,
@@ -1022,10 +1028,20 @@
       SSLConnectionFactory sslConnectionFactory = null;
       if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
       {
+        String clientAlias;
+        if (certNickname.isPresent())
+        {
+          clientAlias = certNickname.getValue();
+        }
+        else
+        {
+          clientAlias = null;
+        }
+
         sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
-                                  keyStorePasswordValue, trustStorePathValue,
-                                  trustStorePasswordValue);
+                                  keyStorePasswordValue, clientAlias,
+                                  trustStorePathValue, trustStorePasswordValue);
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java
index 9792843..126e79a 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPPasswordModify.java
@@ -550,7 +550,7 @@
       {
         SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(sslBlindTrust.isPresent(),
-                                  sslKeyStore.getValue(), keyPIN,
+                                  sslKeyStore.getValue(), keyPIN, null,
                                   sslTrustStore.getValue(), trustPIN);
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
index 0a47f41..d12ba23 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/LDAPSearch.java
@@ -584,6 +584,7 @@
     StringArgument    baseDN                   = null;
     StringArgument    bindDN                   = null;
     StringArgument    bindPassword             = null;
+    StringArgument    certNickname             = null;
     StringArgument    controlStr               = null;
     StringArgument    dereferencePolicy        = null;
     StringArgument    encodingStr              = null;
@@ -690,6 +691,11 @@
                                  MSGID_DESCRIPTION_KEYSTOREPASSWORD_FILE);
       argParser.addArgument(keyStorePasswordFile);
 
+      certNickname = new StringArgument("certnickname", 'N', "certNickname",
+                                        false, false, true, "{nickname}", null,
+                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
+      argParser.addArgument(certNickname);
+
       trustStorePath = new StringArgument("trustStorePath", 'P',
                                   "trustStorePath", false, false, true,
                                   "{trustStorePath}", null, null,
@@ -1315,10 +1321,20 @@
       SSLConnectionFactory sslConnectionFactory = null;
       if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
       {
+        String clientAlias;
+        if (certNickname.isPresent())
+        {
+          clientAlias = certNickname.getValue();
+        }
+        else
+        {
+          clientAlias = null;
+        }
+
         sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(trustAll.isPresent(), keyStorePathValue,
-                                  keyStorePasswordValue, trustStorePathValue,
-                                  trustStorePasswordValue);
+                                  keyStorePasswordValue, clientAlias,
+                                  trustStorePathValue, trustStorePasswordValue);
         connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
       }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/SSLConnectionFactory.java b/opendj-sdk/opends/src/server/org/opends/server/tools/SSLConnectionFactory.java
index 630ed29..ebdf6eb 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/SSLConnectionFactory.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/SSLConnectionFactory.java
@@ -41,6 +41,7 @@
 import javax.net.ssl.TrustManagerFactory;
 
 import org.opends.server.extensions.BlindTrustManagerProvider;
+import org.opends.server.util.SelectableCertificateKeyManager;
 
 import static org.opends.server.messages.ToolMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
@@ -74,6 +75,7 @@
    * @param  keyStorePath        The path to the key store file.
    * @param  keyStorePassword    The PIN to use to access the key store
    *                             contents.
+   * @param  clientAlias         The alias to use for the client certificate.
    * @param  trustStorePath      The path to the trust store file.
    * @param  trustStorePassword  The PIN to use to access the trust store
    *                             contents.
@@ -82,7 +84,7 @@
    *                                  connection factory.
    */
   public void init(boolean trustAll, String keyStorePath,
-                   String keyStorePassword,
+                   String keyStorePassword, String clientAlias,
                    String trustStorePath, String trustStorePassword)
          throws SSLConnectionException
   {
@@ -108,6 +110,12 @@
       {
         keyManagers = getKeyManagers(KeyStore.getDefaultType(), null,
                           keyStorePath, keyStorePassword);
+
+        if (clientAlias != null)
+        {
+          keyManagers = SelectableCertificateKeyManager.wrap(keyManagers,
+                                                             clientAlias);
+        }
       }
 
       ctx.init(keyManagers, trustManagers, new java.security.SecureRandom());
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/StopDS.java b/opendj-sdk/opends/src/server/org/opends/server/tools/StopDS.java
index 6c1bc93..7a7fe2e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/StopDS.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/StopDS.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -176,6 +176,7 @@
     IntegerArgument   port;
     StringArgument    bindDN;
     StringArgument    bindPW;
+    StringArgument    certNickname;
     StringArgument    host;
     StringArgument    keyStoreFile;
     StringArgument    keyStorePW;
@@ -276,6 +277,11 @@
                                              MSGID_STOPDS_DESCRIPTION_KSPWFILE);
       argParser.addArgument(keyStorePWFile);
 
+      certNickname = new StringArgument("certnickname", 'N', "certNickname",
+                                        false, false, true, "{nickname}", null,
+                                        null, MSGID_DESCRIPTION_CERT_NICKNAME);
+      argParser.addArgument(certNickname);
+
       trustStoreFile = new StringArgument("truststorefile", 'P',
                                           "trustStoreFile", false, false, true,
                                           "{trustStoreFile}", null, null,
@@ -490,9 +496,19 @@
     {
       try
       {
+        String clientAlias;
+        if (certNickname.isPresent())
+        {
+          clientAlias = certNickname.getValue();
+        }
+        else
+        {
+          clientAlias = null;
+        }
+
         SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
         sslConnectionFactory.init(trustAll.isPresent(), keyStoreFile.getValue(),
-                                  keyStorePW.getValue(),
+                                  keyStorePW.getValue(), clientAlias,
                                   trustStoreFile.getValue(),
                                   trustStorePW.getValue());
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/SelectableCertificateKeyManager.java b/opendj-sdk/opends/src/server/org/opends/server/util/SelectableCertificateKeyManager.java
new file mode 100644
index 0000000..edfacad
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/SelectableCertificateKeyManager.java
@@ -0,0 +1,315 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Portions Copyright 2007 Sun Microsystems, Inc.
+ */
+package org.opends.server.util;
+
+
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.X509KeyManager;
+
+
+
+/**
+ * This class implements an X.509 key manager that will be used to wrap an
+ * existing key manager and makes it possible to configure which certificate(s)
+ * should be used for client and/or server operations.  The certificate
+ * selection will be based on the alias (also called the nickname) of the
+ * certificate.
+ */
+public class SelectableCertificateKeyManager
+       extends X509ExtendedKeyManager
+{
+  // The alias of the certificate that should be selected from the key manager.
+  private String alias;
+
+  // The key manager that is wrapped by this key manager.
+  private X509KeyManager keyManager;
+
+
+
+  /**
+   * Creates a new instance of this key manager that will wrap the provided key
+   * manager and use the certificate with the specified alias.
+   *
+   * @param  keyManager  The key manager to be wrapped by this key manager.
+   * @param  alias       The nickname of the certificate that should be
+   *                     selected for operations involving this key manager.
+   */
+  public SelectableCertificateKeyManager(X509KeyManager keyManager,
+                                         String alias)
+  {
+    super();
+
+    this.keyManager = keyManager;
+    this.alias      = alias;
+  }
+
+
+
+  /**
+   * Chooses the alias of the client certificate that should be used based on
+   * the provided critieria.  This will either return the preferred alias
+   * configured for this key manager, or {@code null} if no client certificate
+   * with that alias is configured in the underlying key manager.
+   *
+   * @param  keyType  The set of key algorithm names, ordered with the most
+   *                  preferred key type first.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   * @param  socket   The socket to be used for this connection.
+   *
+   * @return  The alias configured for this key manager, or {@code null} if no
+   *          such client certificate is available with that alias.
+   */
+  public String chooseClientAlias(String[] keyType, Principal[] issuers,
+                                  Socket socket)
+  {
+    for (String type : keyType)
+    {
+      String[] clientAliases = keyManager.getClientAliases(type, issuers);
+      if (clientAliases != null)
+      {
+        for (String clientAlias : clientAliases)
+        {
+          if (clientAlias.equals(alias))
+          {
+            return alias;
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Chooses the alias of the client certificate that should be used based on
+   * the provided critieria.  This will either return the preferred alias
+   * configured for this key manager, or {@code null} if no client certificate
+   * with that alias is configured in the underlying key manager.
+   *
+   * @param  keyType  The set of key algorithm names, ordered with the most
+   *                  preferred key type first.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   * @param  engine   The SSL engine to be used for this connection.
+   *
+   * @return  The alias configured for this key manager, or {@code null} if no
+   *          such client certificate is available with that alias.
+   */
+  public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
+                                        SSLEngine engine)
+  {
+    for (String type : keyType)
+    {
+      String[] clientAliases = keyManager.getClientAliases(type, issuers);
+      if (clientAliases != null)
+      {
+        for (String clientAlias : clientAliases)
+        {
+          if (clientAlias.equals(alias))
+          {
+            return alias;
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Chooses the alias of the server certificate that should be used based on
+   * the provided critieria.  This will either return the preferred alias
+   * configured for this key manager, or {@code null} if no server certificate
+   * with that alias is configured in the underlying key manager.
+   *
+   * @param  keyType  The public key type for the certificate.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   * @param  socket   The socket to be used for this connection.
+   *
+   * @return  The alias configured for this key manager, or {@code null} if no
+   *          such server certificate is available with that alias.
+   */
+  public String chooseServerAlias(String keyType, Principal[] issuers,
+                                  Socket socket)
+  {
+    String[] serverAliases = keyManager.getServerAliases(keyType, issuers);
+    if (serverAliases != null)
+    {
+      for (String serverAlias : serverAliases)
+      {
+        if (serverAlias.equals(alias))
+        {
+          return alias;
+        }
+      }
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Chooses the alias of the server certificate that should be used based on
+   * the provided critieria.  This will either return the preferred alias
+   * configured for this key manager, or {@code null} if no server certificate
+   * with that alias is configured in the underlying key manager.
+   *
+   * @param  keyType  The public key type for the certificate.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   * @param  engine   The SSL engine to be used for this connection.
+   *
+   * @return  The alias configured for this key manager, or {@code null} if no
+   *          such server certificate is available with that alias.
+   */
+  public String chooseEngineServerAlias(String keyType, Principal[] issuers,
+                                        SSLEngine engine)
+  {
+    String[] serverAliases = keyManager.getServerAliases(keyType, issuers);
+    if (serverAliases != null)
+    {
+      for (String serverAlias : serverAliases)
+      {
+        if (serverAlias.equals(alias))
+        {
+          return alias;
+        }
+      }
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Retrieves the certificate chain for the provided alias.
+   *
+   * @param  alias  The alias for the certificate chain to retrieve.
+   *
+   * @return  The certificate chain for the provided alias, or {@code null} if
+   *          no certificate is associated with the provided alias.
+   */
+  public X509Certificate[] getCertificateChain(String alias)
+  {
+    return keyManager.getCertificateChain(alias);
+  }
+
+
+
+  /**
+   * Retrieves the set of certificate aliases that may be used for client
+   * authentication with the given public key type and set of issuers.
+   *
+   * @param  keyType  The public key type for the aliases to retrieve.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   *
+   * @return  The set of certificate aliases that may be used for client
+   *          authentication with the given public key type and set of issuers,
+   *          or {@code null} if there were none.
+   */
+  public String[] getClientAliases(String keyType, Principal[] issuers)
+  {
+    return keyManager.getClientAliases(keyType, issuers);
+  }
+
+
+
+  /**
+   * Retrieves the private key for the provided alias.
+   *
+   * @param  alias  The alias for the private key to return.
+   *
+   * @return  The private key for the provided alias, or {@code null} if no
+   *          private key is available for the provided alias.
+   */
+  public PrivateKey getPrivateKey(String alias)
+  {
+    return keyManager.getPrivateKey(alias);
+  }
+
+
+
+  /**
+   * Retrieves the set of certificate aliases that may be used for server
+   * authentication with the given public key type and set of issuers.
+   *
+   * @param  keyType  The public key type for the aliases to retrieve.
+   * @param  issuers  The list of acceptable issuer subject names, or
+   *                  {@code null} if any issuer may be used.
+   *
+   * @return  The set of certificate aliases that may be used for server
+   *          authentication with the given public key type and set of issuers,
+   *          or {@code null} if there were none.
+   */
+  public String[] getServerAliases(String keyType, Principal[] issuers)
+  {
+    return keyManager.getServerAliases(keyType, issuers);
+  }
+
+
+
+  /**
+   * Wraps the provided set of key managers in selectable certificate key
+   * managers using the provided alias.
+   *
+   * @param  keyManagers  The set of key managers to be wrapped.
+   * @param  alias        The alias to use for selecting the desired
+   *                      certificate.
+   *
+   * @return  A key manager array
+   */
+  public static X509ExtendedKeyManager[] wrap(KeyManager[] keyManagers,
+                                              String alias)
+  {
+    X509ExtendedKeyManager[] newKeyManagers =
+         new X509ExtendedKeyManager[keyManagers.length];
+    for (int i=0; i < keyManagers.length; i++)
+    {
+      newKeyManagers[i] = new SelectableCertificateKeyManager(
+                                   (X509KeyManager) keyManagers[i], alias);
+    }
+
+    return newKeyManagers;
+  }
+}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
index 377c0ab..a54b5f4 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPAuthenticationHandlerTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -2471,7 +2471,8 @@
 
 
     SSLConnectionFactory factory = new SSLConnectionFactory();
-    factory.init(false, keyStorePath, "password", trustStorePath, "password");
+    factory.init(false, keyStorePath, "password", "client-cert",
+                 trustStorePath, "password");
 
 
     Socket s = factory.createSocket("127.0.0.1",
@@ -2541,7 +2542,8 @@
 
 
     SSLConnectionFactory factory = new SSLConnectionFactory();
-    factory.init(false, keyStorePath, "password", trustStorePath, "password");
+    factory.init(false, keyStorePath, "password", "client-cert", trustStorePath,
+                 "password");
 
 
     Socket s = factory.createSocket("127.0.0.1",
@@ -2609,7 +2611,8 @@
 
 
     SSLConnectionFactory factory = new SSLConnectionFactory();
-    factory.init(false, keyStorePath, "password", trustStorePath, "password");
+    factory.init(false, keyStorePath, "password", "client-cert", trustStorePath,
+                 "password");
 
 
     Socket s = factory.createSocket("127.0.0.1",
@@ -2683,7 +2686,8 @@
 
 
     SSLConnectionFactory factory = new SSLConnectionFactory();
-    factory.init(false, keyStorePath, "password", trustStorePath, "password");
+    factory.init(false, keyStorePath, "password", "client-cert", trustStorePath,
+                 "password");
 
 
     Socket s = factory.createSocket("127.0.0.1",
@@ -4205,7 +4209,8 @@
 
 
     SSLConnectionFactory factory = new SSLConnectionFactory();
-    factory.init(false, keyStorePath, "password", trustStorePath, "password");
+    factory.init(false, keyStorePath, "password", "client-cert", trustStorePath,
+                 "password");
 
 
     Socket s = factory.createSocket("127.0.0.1",
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
index b47f2c7..2da6066 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPCompareTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -636,6 +636,116 @@
 
 
   /**
+   * Tests a simple LDAP compare over SSL using a trust store and SASL EXTERNAL
+   * authentication when explicitly specifying a valid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleCompareSSLTrustStoreSASLExternalValidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "ds-privilege-name: bypass-acl",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "client-cert",
+      "-P", trustStorePath,
+      "-r",
+      "cn:Test User",
+      "cn=Test User,o=test"
+    };
+
+    assertEquals(LDAPCompare.mainCompare(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests a simple LDAP compare over SSL using a trust store and SASL EXTERNAL
+   * authentication when explicitly specifying an invalid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleCompareSSLTrustStoreSASLExternalInvalidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "ds-privilege-name: bypass-acl",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "invalid",
+      "-P", trustStorePath,
+      "-r",
+      "cn:Test User",
+      "cn=Test User,o=test"
+    };
+
+    assertFalse(LDAPCompare.mainCompare(args, false, null, null) == 0);
+  }
+
+
+
+  /**
    * Tests a simple LDAP compare using StartTLS with a trust store and SASL
    * EXTERNAL authentication.
    *
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
index 212f5fa..b0b096b 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPModifyTestCase.java
@@ -22,7 +22,7 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2006 Sun Microsystems, Inc.
+ *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
  */
 package org.opends.server.tools;
 
@@ -562,6 +562,114 @@
 
 
   /**
+   * Tests a simple modify operation over SSL using a trust store and SASL
+   * EXTERNAL while explicitly specifying a valid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSSLTrustStoreSASLExternalValidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "ds-privilege-name: bypass-acl",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "client-cert",
+      "-P", trustStorePath,
+      "-r",
+      "-f", modifyFilePath
+    };
+
+    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests a simple modify operation over SSL using a trust store and SASL
+   * EXTERNAL while explicitly specifying an invalid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSSLTrustStoreSASLExternalInvalidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "ds-privilege-name: bypass-acl",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "invalid",
+      "-P", trustStorePath,
+      "-r",
+      "-f", modifyFilePath
+    };
+
+    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
+  }
+
+
+
+  /**
    * Tests a simple modify operation with StartTLS using a trust store and SASL
    * EXTERNAL.
    *
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
index b2a00e8..9900bb8 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/tools/LDAPSearchTestCase.java
@@ -714,6 +714,116 @@
 
 
   /**
+   * Tests a simple LDAP search over SSL using a trust store and SASL EXTERNAL
+   * authentication when explicitly specifying a valid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleSearchSSLTrustStoreSASLExternalValidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "client-cert",
+      "-P", trustStorePath,
+      "-r",
+      "-b", "",
+      "-s", "base",
+      "(objectClass=*)"
+    };
+
+    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
+  }
+
+
+
+  /**
+   * Tests a simple LDAP search over SSL using a trust store and SASL EXTERNAL
+   * authentication when explicitly specifying an invalid client certificate.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleSearchSSLTrustStoreSASLExternalInvalidClientCert()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=Test User,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "cn: Test User",
+         "givenName: Test",
+         "sn: User");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    String keyStorePath   = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.keystore";
+    String trustStorePath = DirectoryServer.getServerRoot() + File.separator +
+                            "config" + File.separator + "client.truststore";
+
+    String[] args =
+    {
+      "-h", "127.0.0.1",
+      "-p", String.valueOf(TestCaseUtils.getServerLdapsPort()),
+      "-Z",
+      "-K", keyStorePath,
+      "-W", "password",
+      "-N", "invalid",
+      "-P", trustStorePath,
+      "-r",
+      "-b", "",
+      "-s", "base",
+      "(objectClass=*)"
+    };
+
+    assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);
+  }
+
+
+
+  /**
    * Tests a simple LDAP search using StartTLS with a trust store and SASL
    * EXTERNAL authentication.
    *

--
Gitblit v1.10.0