From 47d00be20f4dd199f7dc444adbd070cca7642be1 Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Tue, 25 Sep 2007 20:27:29 +0000
Subject: [PATCH] Issue 466 (partial) Add some secret key handling code.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java |   46 +++++++++++
 opends/src/server/org/opends/server/types/CryptoManager.java                                 |  158 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+), 0 deletions(-)

diff --git a/opends/src/server/org/opends/server/types/CryptoManager.java b/opends/src/server/org/opends/server/types/CryptoManager.java
index 5df13f2..1f63fc8a 100644
--- a/opends/src/server/org/opends/server/types/CryptoManager.java
+++ b/opends/src/server/org/opends/server/types/CryptoManager.java
@@ -56,11 +56,15 @@
 import org.opends.server.api.Backend;
 import org.opends.server.backends.TrustStoreBackend;
 import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.AddOperation;
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import org.opends.server.loggers.debug.DebugTracer;
 import static org.opends.server.util.StaticUtils.*;
 import org.opends.server.util.Validator;
 import org.opends.server.util.SelectableCertificateKeyManager;
+import org.opends.server.util.StaticUtils;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
 
 /**
  * This class provides the interface to the Directory Server
@@ -220,6 +224,160 @@
 
 
   /**
+   * Returns this instance's instance-key public-key certificate from
+   * the local keystore (i.e., from the truststore-backend and not
+   * from the ADS backed keystore). If the certificate entry does not
+   * yet exist in the truststore backend, the truststore is signaled
+   * to initialized that entry, and the newly generated certificate
+   * is then retrieved and returned.
+   * @return This instance's instance-key public-key certificate from
+   * the local truststore backend.
+   * @throws CryptoManagerException If the certificate cannot be
+   * retrieved.
+   */
+ public byte[] getInstanceKeyCertificate()
+         throws CryptoManagerException {
+   final String DN_ADS_CERTIFICATE = ConfigConstants.ATTR_CERT_ALIAS
+           + "=" + ConfigConstants.ADS_CERTIFICATE_ALIAS + ","
+           + ConfigConstants.DN_TRUST_STORE_ROOT; // TODO: constant
+   final String FILTER_OC_INSTANCE_KEY
+           = new StringBuilder("(objectclass=")
+                              .append(ConfigConstants.OC_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(DirectoryServer.getAttributeType(
+                               ConfigConstants.ATTR_ADS_CERTIFICATE));
+         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);
+ }
+
+
+  /**
+   * Return the identifier of this instance's instance-key. An
+   * instance-key identifier is the MD5 hash of an instance's
+   * instance-key public-key certificate.
+   * @return This instance's instance-key identifier.
+   * @throws CryptoManagerException If there is a problem retrieving
+   * the instance-key public-key certificate or computing its MD5
+   * hash.
+   */
+  public byte[] getInstanceKeyID()
+          throws CryptoManagerException {
+    MessageDigest md;
+    final String mdAlgorithmName = "MD5";
+    try {
+      md = MessageDigest.getInstance(mdAlgorithmName);
+    }
+    catch (NoSuchAlgorithmException ex) {
+      throw new CryptoManagerException(
+              // TODO: i18n
+            Message.raw("Failed to get MessageDigest instance for %s",
+                      mdAlgorithmName), ex);
+    }
+    return md.digest(getInstanceKeyCertificate());
+  }
+
+
+  /**
+   * Unwraps the supplied symmetric key attribute value and re-wraps
+   * it with the public key referred to by the requested instance key
+   * identifier. The symmetric key attribute must be wrapped in this
+   * instance's instance-key-pair public key.
+   * @param symmetricKeyAttribute The symmetric key attribute value to
+   * unwrap and rewrap.
+   * @param requestedInstanceKeyID The key identifier of the public
+   * key to use in the re-wrapping.
+   * @return The symmetric key re-wrapped in the requested public key.
+   * @throws CryptoManagerException If there is a problem unwrapping
+   * the supplied symmetric key attribute value or retrieving the
+   * requested public key.
+   */
+  public byte[] rewrapSymmetricKeyAttribute(
+          final byte[] symmetricKeyAttribute,
+          final byte[] requestedInstanceKeyID)
+          throws CryptoManagerException {
+    final byte[] instanceKeyID = getInstanceKeyID();
+    final byte[] keyIDPrefix = Arrays.copyOf(symmetricKeyAttribute,
+            instanceKeyID.length);
+    if (! Arrays.equals(keyIDPrefix, instanceKeyID)) {
+      throw new CryptoManagerException(
+              // TODO: i18n
+              Message.raw("The instance-key identifier tag %s of" +
+                      " the supplied symmetric key attribute does" +
+                      " not match this instance's instance-key" +
+                      " identifier %s, and hence the symmetric key" +
+                      " cannot be decrypted for processing.",
+         StaticUtils.bytesToColonDelimitedHex(keyIDPrefix),
+         StaticUtils.bytesToColonDelimitedHex(instanceKeyID)));
+    }
+    return symmetricKeyAttribute; // TODO: really unwrap and rewrap
+  }
+
+
+  /**
    * Retrieves the name of the preferred message digest algorithm.
    *
    * @return  The name of the preferred message digest algorithm
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
index c139d10..b95a609 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
@@ -34,6 +34,8 @@
 import org.opends.server.TestCaseUtils;
 
 import org.opends.server.core.DirectoryServer;
+import org.opends.admin.ads.ServerDescriptor;
+import org.opends.admin.ads.util.ConnectionUtils;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -44,6 +46,7 @@
 import java.util.LinkedList;
 import java.util.Arrays;
 import java.lang.reflect.Method;
+import java.security.MessageDigest;
 
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
@@ -51,6 +54,9 @@
 import org.testng.annotations.DataProvider;
 
 import javax.crypto.Mac;
+import javax.naming.directory.*;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.InitialLdapContext;
 
 /**
  This class tests the CryptoManager.
@@ -78,6 +84,42 @@
 
 
   @Test
+  public void testGetInstanceKeyCertificate()
+          throws Exception {
+    final CryptoManager cm = DirectoryServer.getCryptoManager();
+    final byte[] cert = cm.getInstanceKeyCertificate();
+    assertNotNull(cert);
+
+    // The certificate should now be accessible in the truststore backend via LDAP.
+    final InitialLdapContext ctx = ConnectionUtils.createLdapContext(
+            "ldap://" + "127.0.0.1" + ":"
+                    + String.valueOf(TestCaseUtils.getServerLdapPort()),
+            "cn=Directory Manager", "password",
+          ConnectionUtils.getDefaultLDAPTimeout(), null);
+    // TODO: the below dn should 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();
+    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
+    final String attrIDs[] = { "ds-cfg-public-key-certificate;binary" };
+    searchControls.setReturningAttributes(attrIDs);
+    final SearchResult certEntry = ctx.search(dn,
+               "(objectclass=ds-cfg-instance-key)", searchControls).next();
+    final javax.naming.directory.Attribute certAttr
+            = certEntry.getAttributes().get(attrIDs[0]);
+    /* attribute ds-cfg-public-key-certificate is a MUST in the schema */
+    assertNotNull(certAttr);
+    byte[] ldapCert = (byte[])certAttr.get();
+    // Compare the certificate values.
+    assertTrue(Arrays.equals(ldapCert, cert));
+
+    // Compare the MD5 hash of the LDAP attribute with the one
+    // retrieved from the CryptoManager.
+    MessageDigest md = MessageDigest.getInstance("MD5");
+    assertTrue(Arrays.equals(md.digest(ldapCert), cm.getInstanceKeyID()));
+  }
+
+  @Test
   public void testMacSuccess()
           throws Exception {
     final CryptoManager cm = DirectoryServer.getCryptoManager();
@@ -94,6 +136,7 @@
     assertTrue(Arrays.equals(calculatedSignature, signedHash));
   }
 
+  // TODO: other-than-default MAC
 
   /**
    Cipher parameters
@@ -249,4 +292,7 @@
       // ignore - requires Java 6
     }
   }
+
+  // TODO: ensure ciphers using IV output differs for successive
+  // encryptions of the same clear-text.
 }

--
Gitblit v1.10.0