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

coulbeck
02.04.2007 fa24aef24dc4b182e18fc43d4b06916f80b51363
More changes for issue 466.
- Defines the schema for secret keys.
- Keeps the crypto manager secret key cache up to date with secret keys published in ADS.
5 files modified
467 ■■■■■ changed files
opends/resource/schema/02-config.ldif 47 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/TrustStoreBackend.java 30 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/config/ConfigConstants.java 73 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/TrustStoreSyncThread.java 107 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/CryptoManager.java 210 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -2068,6 +2068,36 @@
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.423
  NAME 'ds-cfg-cipher-transformation-name'
  DESC 'The name of a cryptographic cipher transformation consisting of an
  algorithm, a mode, and a padding specification, separated by slashes'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.424
  NAME 'ds-cfg-mac-algorithm-name'
  DESC 'The name of a cryptographic message authentication code (MAC) algorithm'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.425
  NAME 'ds-cfg-key-length-bits'
  DESC 'The length of a cryptographic secret key'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.426
  NAME 'ds-cfg-initialization-vector-length-bits'
  DESC 'The length of a cryptographic cipher initialization vector'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
  SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.427
  NAME 'ds-cfg-symmetric-key'
  DESC 'A cryptographic secret-key wrapped by a public-key'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler'
  SUP top
@@ -3511,4 +3541,19 @@
  SUP ds-cfg-extended-operation-handler
  STRUCTURAL
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.174
  NAME 'ds-cfg-cipher-key'
  SUP top
  STRUCTURAL
  MUST ( ds-cfg-key-id $ ds-cfg-cipher-transformation-name $
  ds-cfg-key-length-bits $ ds-cfg-symmetric-key )
  MAY ( ds-cfg-initialization-vector-length-bits $ ds-cfg-key-compromised-time )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.175
  NAME 'ds-cfg-mac-key'
  SUP top
  STRUCTURAL
  MUST ( ds-cfg-key-id $ ds-cfg-mac-algorithm-name $
  ds-cfg-key-length-bits $ ds-cfg-symmetric-key )
  MAY ds-cfg-key-compromised-time
  X-ORIGIN 'OpenDS Directory Server' )
opends/src/server/org/opends/server/backends/TrustStoreBackend.java
@@ -574,7 +574,7 @@
  {
    // Make sure that the DN specifies a certificate alias.
    AttributeType t =
         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
         DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
    if (v == null)
    {
@@ -615,8 +615,8 @@
    ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
    ObjectClass objectClass =
         DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true);
    ocMap.put(objectClass, OC_INSTANCE_KEY);
         DirectoryServer.getObjectClass(OC_CRYPTO_INSTANCE_KEY, true);
    ocMap.put(objectClass, OC_CRYPTO_INSTANCE_KEY);
    LinkedHashMap<AttributeType,List<Attribute>> opAttrs =
         new LinkedHashMap<AttributeType,List<Attribute>>(0);
@@ -632,7 +632,8 @@
    userAttrs.put(t, attrList);
    t = DirectoryServer.getAttributeType(ATTR_ADS_CERTIFICATE, true);
    t = DirectoryServer.getAttributeType(
         ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true);
    valueSet = new LinkedHashSet<AttributeValue>(1);
    valueSet.add(new AttributeValue(t,
                          certValue));
@@ -792,7 +793,7 @@
      if ((scope != SearchScope.BASE_OBJECT) && (! (aliases.length == 0) ))
      {
        AttributeType certAliasType =
             DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
             DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
        for (String alias : aliases)
        {
          DN certDN = makeChildDN(this.baseDN, certAliasType,
@@ -1570,7 +1571,7 @@
    // Make sure that the DN specifies a certificate alias.
    AttributeType t =
         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
         DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
    if (v == null)
    {
@@ -1612,12 +1613,14 @@
      }
      else
      {
        List<Attribute> certAttrs = entry.getAttribute(ATTR_ADS_CERTIFICATE);
        List<Attribute> certAttrs = entry.getAttribute(
             ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
        if (certAttrs == null)
        {
          Message message =
               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_ATTR.get(
                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
                    String.valueOf(entryDN),
                    ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
          throw new DirectoryException(
               DirectoryServer.getServerErrorResultCode(), message);
        }
@@ -1625,7 +1628,8 @@
        {
          Message message =
               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_ATTRS.get(
                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
                    String.valueOf(entryDN),
                    ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
          throw new DirectoryException(
               DirectoryServer.getServerErrorResultCode(), message);
        }
@@ -1635,7 +1639,8 @@
        {
          Message message =
               ERR_TRUSTSTORE_ENTRY_MISSING_CERT_VALUE.get(
                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
                    String.valueOf(entryDN),
                    ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
          throw new DirectoryException(
               DirectoryServer.getServerErrorResultCode(), message);
        }
@@ -1643,7 +1648,8 @@
        {
          Message message =
               ERR_TRUSTSTORE_ENTRY_HAS_MULTIPLE_CERT_VALUES.get(
                    String.valueOf(entryDN), ATTR_ADS_CERTIFICATE);
                    String.valueOf(entryDN),
                    ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE);
          throw new DirectoryException(
               DirectoryServer.getServerErrorResultCode(), message);
        }
@@ -1699,7 +1705,7 @@
  {
    // Make sure that the DN specifies a certificate alias.
    AttributeType t =
         DirectoryServer.getAttributeType(ATTR_CERT_ALIAS, true);
         DirectoryServer.getAttributeType(ATTR_CRYPTO_KEY_ID, true);
    AttributeValue v = entryDN.getRDN().getAttributeValue(t);
    if (v == null)
    {
opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -2873,9 +2873,9 @@
  /**
   * The name of the attribute that holds a server certificate alias.
   * The name of the attribute that holds a cryptographic cipher-key identifier.
   */
  public static final String ATTR_CERT_ALIAS = "ds-cfg-key-id";
  public static final String ATTR_CRYPTO_KEY_ID = "ds-cfg-key-id";
@@ -2883,7 +2883,7 @@
   * The name of the objectclass that will be used for a server
   * certificate entry.
   */
  public static final String OC_INSTANCE_KEY =
  public static final String OC_CRYPTO_INSTANCE_KEY =
       "ds-cfg-instance-key";
@@ -2898,14 +2898,75 @@
  /**
   * The name of the attribute that is used to specify a server
   * instance key.
   * The name of the objectclass that will be used for a cipher key.
   */
  public static final String ATTR_ADS_CERTIFICATE =
  public static final String OC_CRYPTO_CIPHER_KEY = "ds-cfg-cipher-key";
  /**
   * The name of the objectclass that will be used for a mac key.
   */
  public static final String OC_CRYPTO_MAC_KEY = "ds-cfg-mac-key";
  /**
   * The name of the attribute that is used to hold a cryptographic
   * public key certificate.
   */
  public static final String ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE =
       "ds-cfg-public-key-certificate";
  /**
   * The name of the attribute that is used to hold the name of a
   * cryptographic cipher transformation.
   */
  public static final String ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME =
       "ds-cfg-cipher-transformation-name";
  /**
   * The name of the attribute that is used to hold the name of a
   * cryptographic message authentication code (MAC) algorithm.
   */
  public static final String ATTR_CRYPTO_MAC_ALGORITHM_NAME =
       "ds-cfg-mac-algorithm-name";
  /**
   * The name of the attribute that is used to hold the length of a
   * cryptographic secret key.
   */
  public static final String ATTR_CRYPTO_KEY_LENGTH_BITS =
       "ds-cfg-key-length-bits";
  /**
   * The name of the attribute that is used to hold the length of a
   * cryptographic cipher initialization vector.
   */
  public static final String ATTR_CRYPTO_INIT_VECTOR_LENGTH_BITS =
       "ds-cfg-initialization-vector-length-bits";
  /**
   * The name of the attribute that is used to hold a cryptographic
   * cipher-key wrapped by a public-key.
   */
  public static final String ATTR_CRYPTO_SYMMETRIC_KEY = "ds-cfg-symmetric-key";
  /**
   * The name of the attribute that is used to hold time a cryptographic key
   * was suspected to be compromised.
   */
  public static final String ATTR_CRYPTO_KEY_COMPROMISED_TIME =
       "ds-cfg-key-compromised-time";
  /**
   * The DN of the entry that will serve as the base for all Directory Server
   * loggers.
   */
opends/src/server/org/opends/server/core/TrustStoreSyncThread.java
@@ -46,7 +46,9 @@
import static org.opends.server.util.ServerConstants.
     OID_ENTRY_CHANGE_NOTIFICATION;
import org.opends.server.config.ConfigConstants;
import static org.opends.server.config.ConfigConstants.OC_INSTANCE_KEY;
import static org.opends.server.config.ConfigConstants.OC_CRYPTO_INSTANCE_KEY;
import static org.opends.server.config.ConfigConstants.OC_CRYPTO_CIPHER_KEY;
import static org.opends.server.config.ConfigConstants.OC_CRYPTO_MAC_KEY;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.InternalSearchListener;
@@ -90,6 +92,9 @@
  // The DN of the instance keys container within the admin suffix.
  private DN instanceKeysDN;
  // The DN of the secret keys container within the admin suffix.
  private DN secretKeysDN;
  // The DN of the trust store root.
  private DN trustStoreRootDN;
@@ -102,8 +107,8 @@
  // The attribute type that holds the time a key was compromised.
  AttributeType attrCompromisedTime;
  // A filter on the instance key object class.
  private SearchFilter instanceKeyFilter;
  // A filter on object class to select key entries.
  private SearchFilter keySearchFilter;
  // Indicates whether the ADS suffix backend is initialized.
  private boolean adminBackendInitialized;
@@ -117,6 +122,15 @@
  // Indicates whether the initial search has been done.
  private boolean searchDone;
  // The instance key objectclass.
  private ObjectClass ocInstanceKey;
  // The cipher key objectclass.
  private ObjectClass ocCipherKey;
  // The mac key objectclass.
  private ObjectClass ocMacKey;
  /**
   * Creates a new instance of this trust store synchronization thread.
   */
@@ -140,22 +154,33 @@
    {
      adminSuffixDN = DN.decode(ADSContext.getAdministrationSuffixDN());
      instanceKeysDN = adminSuffixDN.concat(DN.decode("cn=instance keys"));
      secretKeysDN = adminSuffixDN.concat(DN.decode("cn=secret keys"));
      trustStoreRootDN = DN.decode(ConfigConstants.DN_TRUST_STORE_ROOT);
      instanceKeyFilter =
           SearchFilter.createFilterFromString(
                "(objectclass=" + ConfigConstants.OC_INSTANCE_KEY + ")");
      keySearchFilter =
           SearchFilter.createFilterFromString("(|" +
                "(objectclass=" + OC_CRYPTO_INSTANCE_KEY + ")" +
                "(objectclass=" + OC_CRYPTO_CIPHER_KEY + ")" +
                "(objectclass=" + OC_CRYPTO_MAC_KEY + ")" +
                ")");
    }
    catch (DirectoryException e)
    {
      //
    }
    ocInstanceKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_INSTANCE_KEY, true);
    ocCipherKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_CIPHER_KEY, true);
    ocMacKey = DirectoryServer.getObjectClass(
         OC_CRYPTO_MAC_KEY, true);
    attrCert = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_ADS_CERTIFICATE, true);
         ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE, true);
    attrAlias = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CERT_ALIAS, true);
         ConfigConstants.ATTR_CRYPTO_KEY_ID, true);
    attrCompromisedTime = DirectoryServer.getAttributeType(
         "ds-cfg-key-compromised-time", true);
         ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME, true);
    if (DirectoryServer.getBackendWithBaseDN(adminSuffixDN) != null)
    {
@@ -226,7 +251,7 @@
                                     adminSuffixDN, SearchScope.WHOLE_SUBTREE,
                                     DereferencePolicy.NEVER_DEREF_ALIASES,
                                     0, 0,
                                     false, instanceKeyFilter, attributes,
                                     false, keySearchFilter, attributes,
                                     this);
    searchOperation.run();
@@ -403,6 +428,35 @@
                                        SearchResultEntry searchEntry)
       throws DirectoryException
  {
    if (searchEntry.hasObjectClass(ocInstanceKey))
    {
      handleInstanceKeySearchEntry(searchEntry);
    }
    else
    {
      try
      {
        if (searchEntry.hasObjectClass(ocCipherKey))
        {
          DirectoryServer.getCryptoManager().importCipherKeyEntry(searchEntry);
        }
        else if (searchEntry.hasObjectClass(ocMacKey))
        {
          DirectoryServer.getCryptoManager().importMacKeyEntry(searchEntry);
        }
      }
      catch (CryptoManager.CryptoManagerException e)
      {
        throw new DirectoryException(
             DirectoryServer.getServerErrorResultCode(), e);
      }
    }
  }
  private void handleInstanceKeySearchEntry(SearchResultEntry searchEntry)
       throws DirectoryException
  {
    RDN srcRDN = searchEntry.getDN().getRDN();
    // Only process the entry if it has the expected form of RDN.
@@ -545,13 +599,10 @@
   */
  private void addEntry(Entry srcEntry, DN dstDN)
  {
    ObjectClass instanceKeyOC =
         DirectoryServer.getObjectClass(OC_INSTANCE_KEY, true);
    LinkedHashMap<ObjectClass,String> ocMap =
         new LinkedHashMap<ObjectClass,String>(2);
    ocMap.put(DirectoryServer.getTopObjectClass(), OC_TOP);
    ocMap.put(instanceKeyOC, OC_INSTANCE_KEY);
    ocMap.put(ocInstanceKey, OC_CRYPTO_INSTANCE_KEY);
    HashMap<AttributeType, List<Attribute>> userAttrs =
         new HashMap<AttributeType, List<Attribute>>();
@@ -600,11 +651,35 @@
  public void handleAddOperation(PostResponseAddOperation addOperation,
                                 Entry entry)
  {
    if (!addOperation.getEntryDN().isDescendantOf(instanceKeysDN))
    if (addOperation.getEntryDN().isDescendantOf(instanceKeysDN))
    {
      return;
      handleInstanceKeyAddOperation(entry);
    }
    else if (addOperation.getEntryDN().isDescendantOf(secretKeysDN))
    {
      try
      {
        if (entry.hasObjectClass(ocCipherKey))
        {
          DirectoryServer.getCryptoManager().importCipherKeyEntry(entry);
        }
        else if (entry.hasObjectClass(ocMacKey))
        {
          DirectoryServer.getCryptoManager().importMacKeyEntry(entry);
        }
      }
      catch (CryptoManager.CryptoManagerException e)
      {
        Message message = Message.raw("Failed to import key entry: %s",
                                      e.getMessage());
        ErrorLogger.logError(message);
      }
    }
  }
  private void handleInstanceKeyAddOperation(Entry entry)
  {
    RDN srcRDN = entry.getDN().getRDN();
    // Only process the entry if it has the expected form of RDN.
opends/src/server/org/opends/server/types/CryptoManager.java
@@ -66,6 +66,8 @@
import org.opends.server.util.Base64;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.schema.DirectoryStringSyntax;
import org.opends.server.schema.IntegerSyntax;
/**
 * This class provides the interface to the Directory Server
@@ -136,6 +138,17 @@
  // The set of SSL cipher suites enabled or null for the default set.
  private final SortedSet<String> sslCipherSuites;
  // Various schema element references.
  private final AttributeType attrKeyID;
  private final AttributeType attrTransformation;
  private final AttributeType attrMacAlgorithm;
  private final AttributeType attrSymmetricKey;
  private final AttributeType attrInitVectorLength;
  private final AttributeType attrKeyLength;
  private final AttributeType attrCompromisedTime;
  private final ObjectClass   ocCipherKey;
  private final ObjectClass   ocMacKey;
  /**
   * Creates a new instance of this crypto manager object from a given
@@ -155,6 +168,26 @@
  public CryptoManager(CryptoManagerCfg cfg)
         throws ConfigException, InitializationException
  {
    // Initialize various schema references.
    attrKeyID = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_KEY_ID);
    attrTransformation = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_CIPHER_TRANSFORMATION_NAME);
    attrMacAlgorithm = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_MAC_ALGORITHM_NAME);
    attrSymmetricKey = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_SYMMETRIC_KEY);
    attrInitVectorLength = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_INIT_VECTOR_LENGTH_BITS);
    attrKeyLength = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_KEY_LENGTH_BITS);
    attrCompromisedTime = DirectoryServer.getAttributeType(
         ConfigConstants.ATTR_CRYPTO_KEY_COMPROMISED_TIME);
    ocCipherKey = DirectoryServer.getObjectClass(
         ConfigConstants.OC_CRYPTO_CIPHER_KEY);
    ocMacKey = DirectoryServer.getObjectClass(
         ConfigConstants.OC_CRYPTO_MAC_KEY);
    // TODO -- Get the crypto defaults from the configuration.
    // Preferred digest and validation.
@@ -273,13 +306,14 @@
   */
 public byte[] getInstanceKeyCertificate()
         throws CryptoManagerException {
   final String DN_ADS_CERTIFICATE = ConfigConstants.ATTR_CERT_ALIAS
           + "=" + ConfigConstants.ADS_CERTIFICATE_ALIAS + ","
   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_INSTANCE_KEY)
                              .append(")").toString();
        = 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
@@ -307,9 +341,9 @@
         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 List<Attribute> attrs =
            e.getAttribute(DirectoryServer.getAttributeType(
                 ConfigConstants.ATTR_CRYPTO_PUBLIC_KEY_CERTIFICATE));
         final Attribute a = attrs.get(0);
         final AttributeValue v = a.getValues().iterator().next();
         certificate = v.getValueBytes();
@@ -1509,6 +1543,164 @@
  }
  /**
   * Imports a cipher key entry from an entry in ADS.
   *
   * @param entry  The ADS cipher key entry to be imported.
   *               The entry will be ignored if it does not have
   *               the ds-cfg-cipher-key objectclass, or if the
   *               key is already present.
   *
   * @throws CryptoManagerException
   *               If the entry had the correct objectclass,
   *               was not already present but could not
   *               be imported.
   */
  public void importCipherKeyEntry(Entry entry)
       throws CryptoManagerException
  {
    // Ignore the entry if it does not have the appropriate
    // objectclass.
    if (!entry.hasObjectClass(ocCipherKey)) return;
    try
    {
      String keyID =
           entry.getAttributeValue(attrKeyID,
                                   DirectoryStringSyntax.DECODER);
      int ivLengthBits =
           entry.getAttributeValue(attrInitVectorLength,
                                   IntegerSyntax.DECODER);
      int keyLengthBits =
           entry.getAttributeValue(attrKeyLength,
                                   IntegerSyntax.DECODER);
      String transformation =
           entry.getAttributeValue(attrTransformation,
                                   DirectoryStringSyntax.DECODER);
      String compromisedTime =
           entry.getAttributeValue(attrCompromisedTime,
                                   DirectoryStringSyntax.DECODER);
      ArrayList<String> symmetricKeys = new ArrayList<String>();
      entry.getAttributeValues(attrSymmetricKey,
                             DirectoryStringSyntax.DECODER,
                             symmetricKeys);
      // Find the symmetric key value that was wrapped using
      // our instance key.
      SecretKey secretKey = null;
      for (String symmetricKey : symmetricKeys)
      {
        secretKey = decodeSymmetricKeyAttribute(symmetricKey);
        if (secretKey != null) break;
      }
      if (secretKey == null)
      {
        // TODO: i18n
        Message message =
             Message.raw("Key entry %s contains no " +
                  "symmetric key value that can be decoded " +
                  "by this server",
                         entry.getDN());
        throw new CryptoManagerException(message);
      }
      boolean isCompromised = compromisedTime != null;
      CipherKeyEntry.importCipherKeyEntry(this, keyID,
                                          transformation,
                                          secretKey, keyLengthBits,
                                          ivLengthBits,
                                          isCompromised);
    }
    catch (DirectoryException e)
    {
      // TODO: i18n
      Message message =
           Message.raw("Error decoding cipher key entry %s: %s",
                       entry.getDN(), e.getMessage());
      throw new CryptoManagerException(message, e);
    }
  }
  /**
   * Imports a mac key entry from an entry in ADS.
   *
   * @param entry  The ADS mac key entry to be imported. The
   *               entry will be ignored if it does not have the
   *               ds-cfg-mac-key objectclass, or if the key is
   *               already present.
   *
   * @throws CryptoManagerException
   *               If the entry had the correct objectclass,
   *               was not already present but could not
   *               be imported.
   */
  public void importMacKeyEntry(Entry entry)
       throws CryptoManagerException
  {
    // Ignore the entry if it does not have the appropriate
    // objectclass.
    if (!entry.hasObjectClass(ocMacKey)) return;
    try
    {
      String keyID =
           entry.getAttributeValue(attrKeyID,
                                   DirectoryStringSyntax.DECODER);
      int keyLengthBits =
           entry.getAttributeValue(attrKeyLength,
                                   IntegerSyntax.DECODER);
      String algorithm =
           entry.getAttributeValue(attrMacAlgorithm,
                                   DirectoryStringSyntax.DECODER);
      String compromisedTime =
           entry.getAttributeValue(attrCompromisedTime,
                                   DirectoryStringSyntax.DECODER);
      ArrayList<String> symmetricKeys = new ArrayList<String>();
      entry.getAttributeValues(attrSymmetricKey,
                             DirectoryStringSyntax.DECODER,
                             symmetricKeys);
      // Find the symmetric key value that was wrapped using our
      // instance key.
      SecretKey secretKey = null;
      for (String symmetricKey : symmetricKeys)
      {
        secretKey = decodeSymmetricKeyAttribute(symmetricKey);
        if (secretKey != null) break;
      }
      if (secretKey == null)
      {
        // TODO: i18n
        Message message =
             Message.raw("Key entry %s contains no " +
                  "symmetric key value that can be decoded " +
                  "by this server",
                         entry.getDN());
        throw new CryptoManagerException(message);
      }
      boolean isCompromised = compromisedTime != null;
      MacKeyEntry.importMacKeyEntry(this, keyID, algorithm,
                                    secretKey, keyLengthBits,
                                    isCompromised);
    }
    catch (DirectoryException e)
    {
      Message message =
           Message.raw("Error decoding mac key entry %s: %s",
                       entry.getDN(), e.getMessage());
      throw new CryptoManagerException(message, e);
    }
  }
  /**
   * This class implements a utility interface to the unique
   * identifier corresponding to a cryptographic key. For each key
   * stored in an entry in ADS, the key identifier is the naming
@@ -1971,7 +2163,7 @@
            CryptoManager cryptoManager,
            final KeyEntryID keyID) {
      return cryptoManager.cipherKeyEntryCache.get(keyID);
      /* TODO: Does ADS monitorying thread keep map updated with keys
      /* TODO: Does ADS monitoring thread keep map updated with keys
         produced at other sites? If not, fetch from ADS and update
         map (assuming a legitimate key ID, the key should exist in
         ADS because this routine is called for decryption). */