From 92de86f477ec7fd109aeaa0e5a2fd14714ff39d8 Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Thu, 04 Oct 2007 20:07:29 +0000
Subject: [PATCH] Issue 466 (partial) CryptoManager More secret key stuff; testcases.

---
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java                         |   66 ++++++-
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/GetSymmetricKeyExtendedOperationTestCase.java |  105 +++++++++--
 opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java                                                         |  324 ++++++++++++++++++++++++-----------
 3 files changed, 361 insertions(+), 134 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
index 7bc6388..e94d9fa 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
@@ -49,12 +49,12 @@
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.X509ExtendedKeyManager;
 
-import org.opends.server.config.ConfigException;
-import org.opends.server.config.ConfigConstants;
-
+import org.opends.admin.ads.ADSContext;
 import org.opends.server.admin.std.server.CryptoManagerCfg;
 import org.opends.server.api.Backend;
 import org.opends.server.backends.TrustStoreBackend;
+import org.opends.server.config.ConfigException;
+import org.opends.server.config.ConfigConstants;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.core.AddOperation;
 import static org.opends.server.loggers.debug.DebugLogger.*;
@@ -70,7 +70,6 @@
 import org.opends.server.schema.DirectoryStringSyntax;
 import org.opends.server.schema.IntegerSyntax;
 import org.opends.server.schema.BinarySyntax;
-import org.opends.admin.ads.ADSContext;
 
 /**
  * This class provides the interface to the Directory Server
@@ -103,11 +102,15 @@
   private static AttributeType attrInitVectorLength;
   private static AttributeType attrKeyLength;
   private static AttributeType attrCompromisedTime;
+  private static ObjectClass   ocInstanceKey;
   private static ObjectClass   ocCipherKey;
   private static ObjectClass   ocMacKey;
 
-  // The DN of the administration suffix.
-  private static DN adminSuffixDN;
+  // The DN of the local truststore backend.
+  private static DN localTruststoreDN;
+
+  // The DN of the ADS instance keys container.
+  private static DN instanceKeysDN;
 
   // The DN of the ADS secret keys container.
   private static DN secretKeysDN;
@@ -199,6 +202,8 @@
            ConfigConstants.ATTR_CRYPTO_KEY_LENGTH_BITS);
       attrCompromisedTime = DirectoryServer.getAttributeType(
            ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME);
+      ocInstanceKey = DirectoryServer.getObjectClass(
+           ConfigConstants.OC_CRYPTO_INSTANCE_KEY);
       ocCipherKey = DirectoryServer.getObjectClass(
            ConfigConstants.OC_CRYPTO_CIPHER_KEY);
       ocMacKey = DirectoryServer.getObjectClass(
@@ -206,8 +211,12 @@
 
       try
       {
-        adminSuffixDN = DN.decode(
-             ADSContext.getAdministrationSuffixDN());
+        localTruststoreDN
+                = DN.decode(ConfigConstants.DN_TRUST_STORE_ROOT);
+        DN adminSuffixDN = DN.decode(
+                ADSContext.getAdministrationSuffixDN());
+        instanceKeysDN = adminSuffixDN.concat(
+                DN.decode("cn=instance keys"));
         secretKeysDN = adminSuffixDN.concat(
              DN.decode("cn=secret keys"));
       }
@@ -334,84 +343,86 @@
    * @throws CryptoManagerException If the certificate cannot be
    * retrieved.
    */
- public byte[] getInstanceKeyCertificate()
-         throws CryptoManagerException {
-   final String DN_ADS_CERTIFICATE =
-        ConfigConstants.ATTR_CRYPTO_KEY_ID
-        + "=" + ConfigConstants.ADS_CERTIFICATE_ALIAS + ","
-           + ConfigConstants.DN_TRUST_STORE_ROOT; // TODO: constant
-   final String FILTER_OC_INSTANCE_KEY
-        = new StringBuilder("(objectclass=")
-        .append(ConfigConstants.OC_CRYPTO_INSTANCE_KEY)
-        .append(")").toString();
-   final String ATTR_INSTANCE_KEY_CERTIFICATE_BINARY
-           = "ds-cfg-public-key-certificate;binary";
-   final LinkedHashSet<String> requestedAttributes
-           = new LinkedHashSet<String>();
-   requestedAttributes.add(ATTR_INSTANCE_KEY_CERTIFICATE_BINARY);
-   final InternalClientConnection icc
-           = InternalClientConnection.getRootConnection();
-   byte[] certificate = null;
-   try {
-     for (int i = 0; i < 2; ++i) {
-       try {
-         /* If the entry does not exist in the instance's truststore
-            backend, add it (which induces the CryptoManager to create
-            the public-key certificate attribute), then repeat the
-            search. */
-         InternalSearchOperation searchOp = icc.processSearch(
-                 DN.decode(DN_ADS_CERTIFICATE),
-                 SearchScope.BASE_OBJECT,
-                 DereferencePolicy.NEVER_DEREF_ALIASES,
-                 /* size limit */ 0, /* time limit */ 0,
-                 /* types only */ false,
-                 SearchFilter.createFilterFromString(
-                         FILTER_OC_INSTANCE_KEY),
-                 requestedAttributes);
-         final Entry e = searchOp.getSearchEntries().getFirst();
-         /* attribute ds-cfg-public-key-certificate is a MUST in the
-            schema */
-         final List<Attribute> attrs
-                 = e.getAttribute(attrPublicKeyCertificate);
-         final Attribute a = attrs.get(0);
-         final AttributeValue v = a.getValues().iterator().next();
-         certificate = v.getValueBytes();
-         break;
-       }
-       catch (DirectoryException ex) {
-         if (0 == i
-                 && ResultCode.NO_SUCH_OBJECT == ex.getResultCode()){
-           final Entry e = new Entry(DN.decode(DN_ADS_CERTIFICATE),
-                   null, null, null);
-           final AttributeType ocAttrType
-                   = DirectoryServer.getAttributeType("objectclass");
-           e.addObjectClass(new AttributeValue(ocAttrType, "top"));
-           e.addObjectClass(new AttributeValue(ocAttrType,
-                   "ds-cfg-self-signed-cert-request"));
-           AddOperation addOperation = icc.processAdd(e.getDN(),
-                   e.getObjectClasses(),
-                   e.getUserAttributes(),
-                   e.getOperationalAttributes());
-           if (ResultCode.SUCCESS != addOperation.getResultCode()) {
-             throw new DirectoryException(
-                     addOperation.getResultCode(),
-                     Message.raw("Failed to add entry %s.",
-                             e.getDN().toString()));
-           }
-         }
-         else {
-           throw ex;
-         }
-       }
-     }
-   }
-   catch (DirectoryException ex) {
-     throw new CryptoManagerException(
-       // TODO: i18n
-       Message.raw("Failed to retrieve %s.", DN_ADS_CERTIFICATE), ex);
-   }
-   return(certificate);
- }
+  public byte[] getInstanceKeyCertificateFromLocalTruststore()
+          throws CryptoManagerException {
+    // Construct the key entry DN.
+    final AttributeValue distinguishedValue = new AttributeValue(
+            attrKeyID, ConfigConstants.ADS_CERTIFICATE_ALIAS);
+    final DN entryDN = localTruststoreDN.concat(
+            RDN.create(attrKeyID, distinguishedValue));
+    // Construct the search filter.
+    final String FILTER_OC_INSTANCE_KEY =
+            new StringBuilder("(objectclass=")
+                    .append(ocInstanceKey.getNameOrOID())
+                    .append(")").toString();
+    // Construct the attribute list.
+    final LinkedHashSet<String> requestedAttributes
+            = new LinkedHashSet<String>();
+    requestedAttributes.add(attrPublicKeyCertificate.getNameOrOID());
+
+    // Retrieve the certificate from the entry.
+    final InternalClientConnection icc
+            = InternalClientConnection.getRootConnection();
+    byte[] certificate = null;
+    try {
+      for (int i = 0; i < 2; ++i) {
+        try {
+          /* If the entry does not exist in the instance's truststore
+             backend, add it (which induces the CryptoManager to
+             create the public-key certificate attribute), then repeat
+             the search. */
+          InternalSearchOperation searchOp = icc.processSearch(
+                  entryDN,
+                  SearchScope.BASE_OBJECT,
+                  DereferencePolicy.NEVER_DEREF_ALIASES,
+                  /* size limit */ 0, /* time limit */ 0,
+                  /* types only */ false,
+                  SearchFilter.createFilterFromString(
+                          FILTER_OC_INSTANCE_KEY),
+                  requestedAttributes);
+          for (Entry e : searchOp.getSearchEntries()) {
+            /* attribute ds-cfg-public-key-certificate is a MUST in
+               the schema */
+            certificate = e.getAttributeValue(
+                    attrPublicKeyCertificate, BinarySyntax.DECODER);
+            break;
+          }
+          break;
+        }
+        catch (DirectoryException ex) {
+          if (0 == i
+                  && ResultCode.NO_SUCH_OBJECT == ex.getResultCode()){
+            final Entry e = new Entry(entryDN, null, null, null);
+            final AttributeType ocAttrType
+                    = DirectoryServer.getAttributeType("objectclass");
+            e.addObjectClass(new AttributeValue(ocAttrType, "top"));
+            e.addObjectClass(new AttributeValue(ocAttrType,
+                    "ds-cfg-self-signed-cert-request"));
+            AddOperation addOperation = icc.processAdd(e.getDN(),
+                    e.getObjectClasses(),
+                    e.getUserAttributes(),
+                    e.getOperationalAttributes());
+            if (ResultCode.SUCCESS != addOperation.getResultCode()) {
+              throw new DirectoryException(
+                      addOperation.getResultCode(),
+                      Message.raw("Failed to add entry %s.",
+                              e.getDN().toString()));
+            }
+          }
+          else {
+            throw ex;
+          }
+        }
+      }
+    }
+    catch (DirectoryException ex) {
+      throw new CryptoManagerException(
+              // TODO: i18n
+            Message.raw("Failed to retrieve %s.", entryDN.toString()),
+              ex);
+    }
+    return(certificate);
+  }
 
 
   /**
@@ -426,7 +437,8 @@
    */
   public String getInstanceKeyID()
           throws CryptoManagerException {
-    return getInstanceKeyID(getInstanceKeyCertificate());
+    return getInstanceKeyID(
+            getInstanceKeyCertificateFromLocalTruststore());
   }
 
 
@@ -460,12 +472,117 @@
 
 
   /**
+   Publishes the instance key entry in ADS, if it does not already
+   exist.
+
+   TODO: The ADS configuration retrieves an instance's instance key
+   certificate via ServerDescriptor and publishes it via ADSContext in
+   that instance's ADS suffix (in the case a stand-alone instance is
+   being configured) or in an existing ADS suffix (in the case the
+   instance is being added to an existing ADS domain). Instead, have
+   the instance call this routine at startup (after the backends and
+   CryptoManager have been initialized), and change ADS configuration
+   to retrieve the instance key from the ADS suffix in the second
+   case, above (the first case would be unecessary).
+
+   @throws CryptoManagerException In case there is a problem
+   searching for the entry, or, if necessary, adding it.
+
+   @see org.opends.admin.ads.ServerDescriptor
+       #updatePublicKeyCertificate(
+             org.opends.admin.ads.ServerDescriptor,
+             javax.naming.ldap.InitialLdapContext)
+
+   @see org.opends.admin.ads.ADSContext
+       #registerInstanceKeyCertificate(
+             java.util.Map, javax.naming.ldap.LdapName)
+   */
+  public void publishInstanceKeyEntryInADS()
+          throws CryptoManagerException {
+    final byte[] instanceKeyCertificate
+            = getInstanceKeyCertificateFromLocalTruststore();
+    final String instanceKeyID
+            = getInstanceKeyID(instanceKeyCertificate);
+    // Construct the key entry DN.
+    final AttributeValue distinguishedValue =
+            new AttributeValue(attrKeyID, instanceKeyID);
+    final DN entryDN = instanceKeysDN.concat(
+         RDN.create(attrKeyID, distinguishedValue));
+    // Construct the search filter.
+    final String FILTER_OC_INSTANCE_KEY =
+            new StringBuilder("(objectclass=")
+                    .append(ocInstanceKey.getNameOrOID())
+                    .append(")").toString();
+    // Construct the attribute list.
+    final LinkedHashSet<String> requestedAttributes
+            = new LinkedHashSet<String>();
+    requestedAttributes.add("dn");
+
+    // Check for the entry. If it does not exist, create it.
+    final InternalClientConnection icc
+            = InternalClientConnection.getRootConnection();
+    try {
+      final InternalSearchOperation searchOp
+              = icc.processSearch( entryDN, SearchScope.BASE_OBJECT,
+              DereferencePolicy.NEVER_DEREF_ALIASES,
+              /* size limit */ 0, /* time limit */ 0,
+              /* types only */ false,
+              SearchFilter.createFilterFromString(
+                      FILTER_OC_INSTANCE_KEY),
+              requestedAttributes);
+      if (0 == searchOp.getSearchEntries().size()) {
+        final Entry e = new Entry(entryDN, null, null, null);
+        e.addObjectClass(DirectoryServer.getTopObjectClass());
+        e.addObjectClass(ocInstanceKey);
+        // Add the key ID attribute.
+        final LinkedHashSet<AttributeValue> keyIDValueSet =
+                new LinkedHashSet<AttributeValue>(1);
+        keyIDValueSet.add(distinguishedValue);
+        final Attribute keyIDAttr = new Attribute(
+                attrKeyID,
+                attrKeyID.getNameOrOID(),
+                keyIDValueSet);
+        e.addAttribute(keyIDAttr, new ArrayList<AttributeValue>(0));
+        // Add the public key certificate attribute.
+        final LinkedHashSet<AttributeValue> certificateValueSet =
+                new LinkedHashSet<AttributeValue>(1);
+        final AttributeValue certificateValue = new AttributeValue(
+                attrPublicKeyCertificate,
+                ByteStringFactory.create(instanceKeyCertificate));
+        certificateValueSet.add(certificateValue);
+        final Attribute certificateAttr = new Attribute(
+                attrPublicKeyCertificate,
+                attrPublicKeyCertificate.getNameOrOID(),
+                certificateValueSet);
+        e.addAttribute(certificateAttr,
+                new ArrayList<AttributeValue>(0));
+
+        AddOperation addOperation = icc.processAdd(e.getDN(),
+                e.getObjectClasses(),
+                e.getUserAttributes(),
+                e.getOperationalAttributes());
+        if (ResultCode.SUCCESS != addOperation.getResultCode()) {
+          throw new DirectoryException(
+                  addOperation.getResultCode(),
+                  Message.raw("Failed to add entry %s.",
+                          e.getDN().toString()));
+        }
+      }
+    } catch (DirectoryException ex) {
+      throw new CryptoManagerException(
+        // TODO: i18n
+        Message.raw("Failed to publish %s.", entryDN.toString()), ex);
+    }
+  }
+
+
+  /**
    Return the set of valid (i.e., not tagged as compromised) instance
    key-pair public-key certificate entries in ADS.
    @return The set of valid (i.e., not tagged as compromised) instance
    key-pair public-key certificate entries in ADS represented as a Map
-   from ds-cfg-key-id value to ds-cfg-public-key-certificate;binary
-   value. Note that the collection might be empty.
+   from ds-cfg-key-id value to ds-cfg-public-key-certificate value.
+   Note that the collection might be empty.
    @throws CryptoManagerException  In case of a problem with the
    search operation.
    @see org.opends.admin.ads.ADSContext#getTrustedCertificates()
@@ -474,9 +591,8 @@
           throws CryptoManagerException {
     final Map<String, byte[]> certificateMap
             = new HashMap<String, byte[]>();
-    final String baseDNStr = ADSContext.getInstanceKeysContainerDN();
     try {
-      final DN baseDN = DN.decode(baseDNStr);
+      // Construct the search filter.
       final String FILTER_OC_INSTANCE_KEY
               = new StringBuilder("(objectclass=")
               .append(ConfigConstants.OC_CRYPTO_INSTANCE_KEY)
@@ -488,24 +604,24 @@
               .append(FILTER_OC_INSTANCE_KEY)
               .append(FILTER_NOT_COMPROMISED)
               .append(")").toString();
+      // Construct the attribute list.
       final LinkedHashSet<String> requestedAttributes
               = new LinkedHashSet<String>();
       requestedAttributes.add(ConfigConstants.ATTR_CRYPTO_KEY_ID);
-      final String ATTR_INSTANCE_KEY_CERTIFICATE_BINARY
-              = ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE
-              + ";binary";
-      requestedAttributes.add(ATTR_INSTANCE_KEY_CERTIFICATE_BINARY);
+      requestedAttributes.add(
+              attrPublicKeyCertificate.getNameOrOID());
+      // Invoke the search operation.
       final InternalClientConnection icc
               = InternalClientConnection.getRootConnection();
       InternalSearchOperation searchOp = icc.processSearch(
-              baseDN,
+              instanceKeysDN,
               SearchScope.SINGLE_LEVEL,
               DereferencePolicy.NEVER_DEREF_ALIASES,
               /* size limit */ 0, /* time limit */ 0,
               /* types only */ false,
               SearchFilter.createFilterFromString(searchFilter),
               requestedAttributes);
-
+      // Evaluate the search response.
       for (Entry e : searchOp.getSearchEntries()) {
         /* attribute ds-cfg-key-id is the RDN and attribute
            ds-cfg-public-key-certificate is a MUST in the schema */
@@ -521,7 +637,7 @@
               // TODO: i18n
               Message.raw("Error retrieving instance-key public key" +
                       " certificates from ADS container %s.",
-                      baseDNStr), ex);
+                      instanceKeysDN.toString()), ex);
     }
     return(certificateMap);
   }
@@ -742,15 +858,16 @@
     final SecretKey secretKey
             = decodeSymmetricKeyAttribute(symmetricKeyAttribute);
     final Map<String, byte[]> certMap = getTrustedCertificates();
-    if (! certMap.containsKey(requestedInstanceKeyID)) {
+    if (! (certMap.containsKey(requestedInstanceKeyID)
+            && null != certMap.get(requestedInstanceKeyID))) {
       throw new CryptoManagerException(
               // TODO: i18n
               Message.raw("The public key certificate specified by" +
                       " the identifier %s cannot be found.",
                       requestedInstanceKeyID));
     }
-    final byte[] wrappingKeyCert
-            = certMap.get(requestedInstanceKeyID);
+    final byte[] wrappingKeyCert =
+            certMap.get(requestedInstanceKeyID);
     return encodeSymmetricKeyAttribute(
             requestedInstanceKeyID, wrappingKeyCert, secretKey);
   }
@@ -2220,7 +2337,7 @@
 
       // Need to add our own instance certificate.
       byte[] instanceKeyCertificate =
-           cryptoManager.getInstanceKeyCertificate();
+         cryptoManager.getInstanceKeyCertificateFromLocalTruststore();
       trustedCerts.put(getInstanceKeyID(instanceKeyCertificate),
                        instanceKeyCertificate);
 
@@ -2263,6 +2380,7 @@
       }
     }
 
+
     /**
      * Initializes a secret key entry from the supplied parameters,
      * validates it, and registers it in the supplied map. The
@@ -2684,7 +2802,7 @@
 
       // Need to add our own instance certificate.
       byte[] instanceKeyCertificate =
-           cryptoManager.getInstanceKeyCertificate();
+         cryptoManager.getInstanceKeyCertificateFromLocalTruststore();
       trustedCerts.put(getInstanceKeyID(instanceKeyCertificate),
                        instanceKeyCertificate);
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/GetSymmetricKeyExtendedOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/GetSymmetricKeyExtendedOperationTestCase.java
index a76eebd..9464bd5 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/GetSymmetricKeyExtendedOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/GetSymmetricKeyExtendedOperationTestCase.java
@@ -30,16 +30,22 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.opends.server.TestCaseUtils;
-import org.opends.server.types.ResultCode;
-import org.opends.server.types.CryptoManager;
+import org.opends.server.schema.DirectoryStringSyntax;
+import org.opends.server.config.ConfigConstants;
+import org.opends.server.types.*;
 import org.opends.server.core.ExtendedOperation;
 import org.opends.server.core.DirectoryServer;
 import org.opends.server.util.ServerConstants;
+import org.opends.server.util.TimeThread;
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.admin.ads.ADSContext;
 import static org.testng.Assert.*;
 import static org.testng.Assert.assertEquals;
 
+import java.util.LinkedHashSet;
+
 /**
  * A set of test cases for the symmetric key extended operation.
  */
@@ -60,32 +66,93 @@
 
 
 
-  @Test(enabled=false)
+  @Test(enabled=true)
   public void testValidRequest() throws Exception
   {
-    CryptoManager cm = DirectoryServer.getCryptoManager();
+    final CryptoManager cm = DirectoryServer.getCryptoManager();
+    final String secretMessage = "zyxwvutsrqponmlkjihgfedcba";
+    final String cipherTransformationName = "AES/CBC/PKCS5Padding";
+    final int cipherKeyLength = 128;
 
-    // TODO use a proper symmetric key value
-    String symmetricKey = cm.getInstanceKeyID();
-    String instanceKeyID =  cm.getInstanceKeyID();
+    cm.publishInstanceKeyEntryInADS();
 
-    ASN1OctetString requestValue =
-         GetSymmetricKeyExtendedOperation.encodeRequestValue(
-              symmetricKey, instanceKeyID);
+    // Initial encryption ensures a cipher key entry is in ADS.
+    final byte[] cipherText = cm.encrypt(cipherTransformationName,
+            cipherKeyLength, secretMessage.getBytes());
 
-    InternalClientConnection internalConnection =
+    // Retrieve all uncompromised cipher key entries corresponding to the
+    // specified transformation and key length.
+    final String baseDNStr // TODO: is this DN defined elsewhere as a constant?
+            = "cn=secret keys," + ADSContext.getAdministrationSuffixDN();
+    final DN baseDN = DN.decode(baseDNStr);
+    final String FILTER_OC_INSTANCE_KEY
+            = new StringBuilder("(objectclass=")
+            .append(ConfigConstants.OC_CRYPTO_CIPHER_KEY)
+            .append(")").toString();
+    final String FILTER_NOT_COMPROMISED = new StringBuilder("(!(")
+            .append(ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME)
+            .append("=*))").toString();
+    final String FILTER_CIPHER_TRANSFORMATION_NAME = new StringBuilder("(")
+            .append(ConfigConstants.ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME)
+            .append("=").append(cipherTransformationName)
+            .append(")").toString();
+    final String FILTER_CIPHER_KEY_LENGTH = new StringBuilder("(")
+            .append(ConfigConstants.ATTR_CRYPTO_KEY_LENGTH_BITS)
+            .append("=").append(String.valueOf(cipherKeyLength))
+            .append(")").toString();
+    final String searchFilter = new StringBuilder("(&")
+            .append(FILTER_OC_INSTANCE_KEY)
+            .append(FILTER_NOT_COMPROMISED)
+            .append(FILTER_CIPHER_TRANSFORMATION_NAME)
+            .append(FILTER_CIPHER_KEY_LENGTH)
+            .append(")").toString();
+    final LinkedHashSet<String> requestedAttributes
+            = new LinkedHashSet<String>();
+    requestedAttributes.add(ConfigConstants.ATTR_CRYPTO_SYMMETRIC_KEY);
+    final InternalClientConnection icc
+            = InternalClientConnection.getRootConnection();
+    InternalSearchOperation searchOp = icc.processSearch(
+            baseDN,
+            SearchScope.SINGLE_LEVEL,
+            DereferencePolicy.NEVER_DEREF_ALIASES,
+            /* size limit */ 0, /* time limit */ 0,
+            /* types only */ false,
+            SearchFilter.createFilterFromString(searchFilter),
+            requestedAttributes);
+    assertTrue(0 < searchOp.getSearchEntries().size());
+
+    final InternalClientConnection internalConnection =
          InternalClientConnection.getRootConnection();
-    ExtendedOperation extendedOperation =
-         internalConnection.processExtendedOperation(
-              ServerConstants.OID_GET_SYMMETRIC_KEY_EXTENDED_OP, requestValue);
-
-    assertEquals(extendedOperation.getResultCode(), ResultCode.SUCCESS);
-    assertEquals(extendedOperation.getResponseValue().stringValue(),
-                 symmetricKey);
+    final String instanceKeyID = cm.getInstanceKeyID();
+    final AttributeType attrSymmetricKey = DirectoryServer.getAttributeType(
+         ConfigConstants.ATTR_CRYPTO_SYMMETRIC_KEY);
+    for (Entry e : searchOp.getSearchEntries()) {
+      final String symmetricKeyAttributeValue
+              = e.getAttributeValue(attrSymmetricKey, DirectoryStringSyntax.DECODER);
+      final ASN1OctetString requestValue =
+           GetSymmetricKeyExtendedOperation.encodeRequestValue(
+                symmetricKeyAttributeValue, instanceKeyID);
+      final ExtendedOperation extendedOperation =
+              internalConnection.processExtendedOperation(
+                      ServerConstants.OID_GET_SYMMETRIC_KEY_EXTENDED_OP,
+                      requestValue);
+      assertEquals(extendedOperation.getResultCode(), ResultCode.SUCCESS);
+      // The key should be re-wrapped, and hence have a different binary
+      // representation....
+      final String responseValue
+              = extendedOperation.getResponseValue().stringValue();
+      assertFalse(symmetricKeyAttributeValue.equals(responseValue));
+      // ... but the keyIDs should be equal (ideally, the validity of
+      // the returned value would be checked by decoding the
+      // returned ds-cfg-symmetric-key attribute value; however, there
+      // is no non-private method to call.
+      assertEquals(responseValue.split(":")[0],
+              symmetricKeyAttributeValue.split(":")[0]);
+    }
   }
 
 
-  @Test(enabled=false)
+  @Test()
   public void testInvalidRequest() throws Exception
   {
     CryptoManager cm = DirectoryServer.getCryptoManager();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
index 973cf74..a07c4be 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
@@ -85,8 +85,7 @@
    */
   @AfterClass()
   public void CleanUp() throws Exception {
-    DirectoryServer.restart(this.getClass().getName(),
-            Message.raw("CryptoManager: clean-up internal key caches."));
+    // TODO: remove at least secret key entries added in this exercise.
   }
 
 
@@ -94,7 +93,7 @@
   public void testGetInstanceKeyCertificate()
           throws Exception {
     final CryptoManager cm = DirectoryServer.getCryptoManager();
-    final byte[] cert = cm.getInstanceKeyCertificate();
+    final byte[] cert = cm.getInstanceKeyCertificateFromLocalTruststore();
     assertNotNull(cert);
 
     // The certificate should now be accessible in the truststore backend via LDAP.
@@ -103,7 +102,7 @@
                     + String.valueOf(TestCaseUtils.getServerLdapPort()),
             "cn=Directory Manager", "password",
           ConnectionUtils.getDefaultLDAPTimeout(), null);
-    // TODO: the below dn should be in ConfigConstants
+    // TODO: should the below dn be in ConfigConstants?
     final String dnStr = "ds-cfg-key-id=ads-certificate,cn=ads-truststore";
     final LdapName dn = new LdapName(dnStr);
     final SearchControls searchControls = new SearchControls();
@@ -125,6 +124,10 @@
     MessageDigest md = MessageDigest.getInstance("MD5");
     assertTrue(StaticUtils.bytesToHexNoSpace(
          md.digest(ldapCert)).equals(cm.getInstanceKeyID()));
+
+    // Call twice to ensure idempotent. 
+    cm.publishInstanceKeyEntryInADS();
+    cm.publishInstanceKeyEntryInADS();
   }
 
   @Test
@@ -342,12 +345,14 @@
 
 
   /**
-   Mark a key compromised; ensure 1) subsequent encryption requests use a new
-   key; 2) ciphertext produced using the compromised key can still be decrypted.
+   Mark a key compromised; ensure 1) subsequent encryption requests use a
+   new key; 2) ciphertext produced using the compromised key can still be
+   decrypted; 3) once the compromised key entry is removed, confirm ciphertext
+   produced using the compromised key can no longer be decrypted.
 
    @throws Exception In case something exceptional happens.
    */
-  @Test(enabled=true)
+  @Test()
   public void testCompromisedKey() throws Exception {
     final CryptoManager cm = DirectoryServer.getCryptoManager();
     final String secretMessage = "zyxwvutsrqponmlkjihgfedcba";
@@ -360,7 +365,7 @@
 
     // Retrieve all uncompromised cipher key entries corresponding to the
     // specified transformation and key length. Mark each entry compromised.
-    final String baseDNStr // TODO: is this DN a constant?
+    final String baseDNStr // TODO: is this DN defined elsewhere as a constant?
             = "cn=secret keys," + ADSContext.getAdministrationSuffixDN();
     final DN baseDN = DN.decode(baseDNStr);
     final String FILTER_OC_INSTANCE_KEY
@@ -397,8 +402,8 @@
             /* types only */ false,
             SearchFilter.createFilterFromString(searchFilter),
             requestedAttributes);
-
     assertTrue(0 < searchOp.getSearchEntries().size());
+
     String compromisedTime = TimeThread.getGeneralizedTime();
     for (Entry e : searchOp.getSearchEntries()) {
       TestCaseUtils.applyModifications(
@@ -414,18 +419,55 @@
     final byte[] cipherText2 = cm.encrypt(cipherTransformationName,
             cipherKeyLength, secretMessage.getBytes());
 
-    // test for identical keys
+    // 1. Test for distinct keys.
     final byte[] keyID = new byte[16];
     final byte[] keyID2 = new byte[16];
     System.arraycopy(cipherText, 0, keyID, 0, 16);
     System.arraycopy(cipherText2, 0, keyID2, 0, 16);
     assertTrue(! Arrays.equals(keyID, keyID2));
 
-    // confirm ciphertext produced using compromised key can still
-    // be decrypted.
+    // 2. Confirm ciphertext produced using the compromised key can still be
+    // decrypted.
     final byte[] plainText = cm.decrypt(cipherText);
     assertEquals((new String(plainText)), secretMessage);
 
+    // 3. Delete the compromised entry(ies) and ensure ciphertext produced
+    // using a compromised key can no longer be decrypted.
+    for (Entry e : searchOp.getSearchEntries()) {
+      TestCaseUtils.applyModifications(
+        "dn: " + e.getDN().toNormalizedString(),
+        "changetype: delete");
+    }
+    Thread.sleep(1000); // Clearing the cache is asynchronous.
+    try {
+      cm.decrypt(cipherText);
+    }
+    catch (CryptoManager.CryptoManagerException ex) {
+      // TODO: if reasons are added to CryptoManagerException, check for
+      // expected cause.
+    }
   }
 
+  /**
+   TODO: Test the secret key synchronization protocol.
+
+     1. Create the first instance; add reversible password storage scheme
+     to password policy; add entry using explicit password policy; confirm
+     secret key entry has been produced.
+
+     2. Create and initialize the second instance into the existing ADS domain.
+     The secret key entries should be propagated to the second instance via
+     replication. Then the new instance should detect that the secret key
+     entries are missing ds-cfg-symmetric-key attribute values for that
+     instance, inducing the key synchronization protocol.
+
+     3. Confirm the second instance can decrypt the password of the entry
+     added in step 1; e.g., bind as that user.
+
+     4. Stop the second instance. At the first instance, enable a different
+     reversible password storage scheme (different cipher transformation,
+     and hence secret key entry); add another entry using that password
+     storage scheme; start the second instance; ensure the password can
+     be decrypted at the second instance.
+     */
 }

--
Gitblit v1.10.0