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

david_page
14.32.2007 0b85fb3e6530e91e62c332e987c3283f50a3e2b1
No issue.
CryptoManager
Step 3. Factor interface from CryptoManager implementation.
1 files added
1 files renamed
18 files modified
2228 ■■■■ changed files
opends/src/ads/org/opends/admin/ads/ADSContext.java 2 ●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/SchemaBackend.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/BackupManager.java 1 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/JebFormat.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java 1697 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/crypto/CryptoManagerSync.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperation.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/AESPasswordStorageScheme.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/BlowfishPasswordStorageScheme.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java 25 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/RC4PasswordStorageScheme.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/TripleDESPasswordStorageScheme.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/CryptoManager.java 423 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/CryptoManagerException.java 5 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/DirectoryConfig.java 1 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/crypto/CryptoManagerTestCase.java 10 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java 7 ●●●●● patch | view | raw | blame | history
opends/src/ads/org/opends/admin/ads/ADSContext.java
@@ -2172,7 +2172,7 @@
   value to ds-cfg-public-key-certificate;binary value. Note that the collection
   might be empty.
   @throws ADSContextException in case of problems with the entry search.
   @see org.opends.server.crypto.CryptoManager#getTrustedCertificates
   @see org.opends.server.crypto.CryptoManagerImpl#getTrustedCertificates
   */
  public Map<String,byte[]> getTrustedCertificates()
          throws ADSContextException
opends/src/ads/org/opends/admin/ads/ADSContextHelper.java
@@ -50,7 +50,7 @@
import org.opends.server.admin.std.meta.BackendCfgDefn;
import org.opends.server.admin.std.meta.LDIFBackendCfgDefn;
import org.opends.server.config.ConfigConstants;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.crypto.CryptoManagerImpl;
import org.opends.server.types.CryptoManagerException;
import org.opends.server.types.DN;
@@ -252,7 +252,7 @@
      else {
        /* create key ID, if it was not supplied in serverProperties */
        if (null == keyID) {
          keyID = CryptoManager.getInstanceKeyID(
          keyID = CryptoManagerImpl.getInstanceKeyID(
              (byte[])serverProperties.get(
                  ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE));
          keyAttrs.put(new BasicAttribute(
opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -80,7 +80,6 @@
import org.opends.server.schema.MatchingRuleUseSyntax;
import org.opends.server.schema.NameFormSyntax;
import org.opends.server.schema.ObjectClassSyntax;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.CryptoManagerException;
import org.opends.server.types.*;
import org.opends.server.util.DynamicConstants;
opends/src/server/org/opends/server/backends/jeb/BackupManager.java
@@ -30,7 +30,6 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.util.DynamicConstants;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.CryptoManagerException;
import javax.crypto.Mac;
opends/src/server/org/opends/server/backends/jeb/JebFormat.java
@@ -34,11 +34,7 @@
import org.opends.server.protocols.asn1.ASN1Integer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.protocols.asn1.ASN1Sequence;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.EntryEncodeConfig;
import org.opends.server.types.LDAPException;
import org.opends.server.types.*;
import static org.opends.server.loggers.debug.DebugLogger.*;
import org.opends.server.loggers.debug.DebugTracer;
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -145,7 +145,7 @@
import org.opends.server.types.AttributeValue;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.Control;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.crypto.CryptoManagerImpl;
import org.opends.server.types.DITContentRule;
import org.opends.server.types.DITStructureRule;
import org.opends.server.types.DN;
@@ -521,7 +521,7 @@
  private CoreConfigManager coreConfigManager;
  // The crypto manager for the Directory Server.
  private CryptoManager cryptoManager;
  private CryptoManagerImpl cryptoManager;
  // The default compressed schema manager.
  private DefaultCompressedSchema compressedSchema;
@@ -2211,7 +2211,7 @@
    RootCfg root =
         ServerManagementContext.getInstance().getRootConfiguration();
    CryptoManagerCfg cryptoManagerCfg = root.getCryptoManager();
    cryptoManager = new CryptoManager(cryptoManagerCfg);
    cryptoManager = new CryptoManagerImpl(cryptoManagerCfg);
  }
@@ -2221,7 +2221,7 @@
   *
   * @return  A reference to the Directory Server crypto manager.
   */
  public static CryptoManager getCryptoManager()
  public static CryptoManagerImpl getCryptoManager()
  {
    return directoryServer.cryptoManager;
  }
opends/src/server/org/opends/server/crypto/CryptoManagerImpl.java
File was renamed from opends/src/server/org/opends/server/crypto/CryptoManager.java
@@ -103,19 +103,15 @@
 are a lot of similarities and it is conceivable at some point that
 accelerated compression may be available just as it is for
 cryptographic operations.
 <p>
 Other components of CryptoManager:
 @see "src/admin/defn/org/opends/server/admin/std\
                                      /CryptoManagerConfiguration.xml"
 @see org.opends.server.crypto.CryptoManagerSync
 @see org.opends.server.crypto.GetSymmetricKeyExtendedOperation
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public class CryptoManager
     implements ConfigurationChangeListener<CryptoManagerCfg>
public class CryptoManagerImpl
        implements ConfigurationChangeListener<CryptoManagerCfg>, CryptoManager
{
  /**
   * The tracer object for the debug logger.
@@ -131,7 +127,7 @@
  private static AttributeType attrInitVectorLength;
  private static AttributeType attrKeyLength;
  private static AttributeType attrCompromisedTime;
  private static ObjectClass ocCertRequest;
  private static ObjectClass   ocCertRequest;
  private static ObjectClass   ocInstanceKey;
  private static ObjectClass   ocCipherKey;
  private static ObjectClass   ocMacKey;
@@ -215,7 +211,7 @@
   occurs while creating this {@code CryptoManager} that is not the result of a
   problem in the configuration.
   */
  public CryptoManager(CryptoManagerCfg cfg)
  public CryptoManagerImpl(CryptoManagerCfg cfg)
         throws ConfigException, InitializationException {
    if (!schemaInitDone) {
      // Initialize various schema references.
@@ -464,6 +460,35 @@
  /**
   * Retrieve the ADS trust store backend.
   * @return The ADS trust store backend.
   * @throws ConfigException If the ADS trust store backend is
   *                         not configured.
   */
  private TrustStoreBackend getTrustStoreBackend()
       throws ConfigException
  {
    Backend b = DirectoryServer.getBackend(
         ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
    if (b == null)
    {
      Message msg =
           ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_NOT_ENABLED.get(
                ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
      throw new ConfigException(msg);
    }
    if (!(b instanceof TrustStoreBackend))
    {
      Message msg =
           ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_WRONG_CLASS.get(
                ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
      throw new ConfigException(msg);
    }
    return (TrustStoreBackend)b;
  }
  /**
   * 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
@@ -1014,878 +1039,120 @@
  /**
   * Retrieves the name of the preferred message digest algorithm.
   * Given a set of other servers' symmetric key values for
   * a given secret key, use the Get Symmetric Key extended
   * operation to request this server's symmetric key value.
   *
   * @return  The name of the preferred message digest algorithm
   * @param  symmetricKeys  The known symmetric key values for
   *                        a given secret key.
   *
   * @return The symmetric key value for this server, or null if
   *         none could be obtained.
   */
  public String getPreferredMessageDigestAlgorithm()
  private String getSymmetricKey(List<String> symmetricKeys)
  {
    return preferredDigestAlgorithm;
  }
  /**
   * Retrieves a <CODE>MessageDigest</CODE> object that may be used to
   * generate digests using the preferred digest algorithm.
   *
   * @return  A <CODE>MessageDigest</CODE> object that may be used to
   *          generate digests using the preferred digest algorithm.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public MessageDigest getPreferredMessageDigest()
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(preferredDigestAlgorithm);
  }
  /**
   * Retrieves a <CODE>MessageDigest</CODE> object that may be used to
   * generate digests using the specified algorithm.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   *
   * @return  A <CODE>MessageDigest</CODE> object that may be used to
   *          generate digests using the specified algorithm.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public MessageDigest getMessageDigest(String digestAlgorithm)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(digestAlgorithm);
  }
  /**
   * Retrieves a byte array containing a message digest based on the
   * provided data, using the preferred digest algorithm.
   *
   * @param  data  The data to be digested.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public byte[] digest(byte[] data)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(preferredDigestAlgorithm).
                digest(data);
  }
  /**
   * Retrieves a byte array containing a message digest based on the
   * provided data, using the requested digest algorithm.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   * @param  data             The data to be digested.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public byte[] digest(String digestAlgorithm, byte[] data)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(digestAlgorithm).digest(data);
  }
  /**
   * Retrieves a byte array containing a message digest based on the
   * data read from the provided input stream, using the preferred
   * digest algorithm.  Data will be read until the end of the stream
   * is reached.
   *
   * @param  inputStream  The input stream from which the data is to
   *                      be read.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  IOException  If a problem occurs while reading data from
   *                       the provided stream.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public byte[] digest(InputStream inputStream)
         throws IOException, NoSuchAlgorithmException
  {
    MessageDigest digest =
         MessageDigest.getInstance(preferredDigestAlgorithm);
    byte[] buffer = new byte[8192];
    while (true)
    InternalClientConnection internalConnection =
         InternalClientConnection.getRootConnection();
    for (String symmetricKey : symmetricKeys)
    {
      int bytesRead = inputStream.read(buffer);
      if (bytesRead < 0)
      try
      {
        break;
      }
        // Get the server instance key ID from the symmetric key.
        String[] elements = symmetricKey.split(":", 0);
        String instanceKeyID = elements[0];
      digest.update(buffer, 0, bytesRead);
    }
        // Find the server entry from the instance key ID.
        String filter = "(" +
             ConfigConstants.ATTR_CRYPTO_KEY_ID + "=" +
             instanceKeyID + ")";
        InternalSearchOperation internalSearch =
             internalConnection.processSearch(
                  serversDN, SearchScope.SUBORDINATE_SUBTREE,
                  SearchFilter.createFilterFromString(filter));
        if (internalSearch.getResultCode() != ResultCode.SUCCESS)
          continue;
    return digest.digest();
  }
  /**
   * Retrieves a byte array containing a message digest based on the
   * data read from the provided input stream, using the requested
   * digest algorithm.  Data will be read until the end of the stream
   * is reached.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   * @param  inputStream      The input stream from which the data is
   *                          to be read.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  IOException  If a problem occurs while reading data from
   *                       the provided stream.
   *
   * @throws  NoSuchAlgorithmException  If the requested algorithm is
   *                                    not supported or is
   *                                    unavailable.
   */
  public byte[] digest(String digestAlgorithm,
                       InputStream inputStream)
         throws IOException, NoSuchAlgorithmException
  {
    MessageDigest digest = MessageDigest.getInstance(digestAlgorithm);
    byte[] buffer = new byte[8192];
    while (true)
    {
      int bytesRead = inputStream.read(buffer);
      if (bytesRead < 0)
      {
        break;
      }
      digest.update(buffer, 0, bytesRead);
    }
    return digest.digest();
  }
  /**
   * For the current preferred MAC algorithm and key length, return
   * the identifier of the corresponding key entry. Note: the result
   * (key identifier) might change across invocations, due to either
   * of the perferred parameters changing, or because the original
   * key was marked compromised and a replacement key generated.
   *
   * @return A String representation of the identifier of a key entry
   * corresponding to the preferred MAC algorithm and key length.
   *
   * @throws CryptoManagerException In case one or more of the key
   * parameters is invalid, or there is a problem instantiating the
   * key entry in case it does not already exist.
   */
  public String getMacEngineKeyEntryID()
          throws CryptoManagerException
  {
    return getMacEngineKeyEntryID(preferredMACAlgorithm,
            preferredMACAlgorithmKeyLengthBits);
  }
  /**
   * For the specified MAC algorithm and key length, return
   * the identifier of the corresponding key entry. Note: the result
   * (key identifier) might change across invocations, due to either
   * of the perferred parameters changing, or because the original
   * key was marked compromised and a replacement key generated.
   *
   * @param  macAlgorithm  The algorithm to use for the MAC engine.
   *
   * @param  keyLengthBits  The key length in bits to use with the
   *         specified algorithm.
   *
   * @return A String representation of the identifier of a key entry
   * corresponding to the specified MAC algorithm and key length.
   *
   * @throws CryptoManagerException In case one or more of the key
   * parameters is invalid, or there is a problem instantiating the
   * key entry in case it does not already exist.
   */
  public String getMacEngineKeyEntryID(final String macAlgorithm,
                                       final int keyLengthBits)
         throws CryptoManagerException {
    Validator.ensureNotNull(macAlgorithm);
    MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this, macAlgorithm,
                                                   keyLengthBits);
    if (null == keyEntry) {
      keyEntry = MacKeyEntry.generateKeyEntry(this, macAlgorithm,
                                              keyLengthBits);
    }
    return keyEntry.getKeyID().getStringValue();
  }
  /**
   * For the specified key entry identifier, instantiate a MAC engine.
   *
   * @param keyEntryID The identifier of the key entry containing the
   * desired MAC algorithm name and key length.
   *
   * @return The MAC engine instantiated with the parameters from the
   * referenced key entry, or null if no such entry exists.
   *
   * @throws CryptoManagerException  In case the key entry identifier
   * is invalid or there is a problem instatiating the MAC engine from
   * the parameters in the referenced key entry.
   */
  public Mac getMacEngine(String keyEntryID)
          throws CryptoManagerException
  {
    final MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this,
            new KeyEntryID(keyEntryID));
    return (null == keyEntry) ? null : getMacEngine(keyEntry);
  }
  /**
   * This method produces an initialized MAC engine based on the
   * supplied MacKeyEntry's state.
   *
   * @param keyEntry The MacKeyEntry specifying the Mac properties.
   *
   * @return  An initialized Mac object.
   *
   * @throws CryptoManagerException  In case there was a error
   * instantiating the Mac object.
   */
  private static Mac getMacEngine(MacKeyEntry keyEntry)
          throws CryptoManagerException
  {
    Mac mac;
    try {
      mac = Mac.getInstance(keyEntry.getType());
    }
    catch (NoSuchAlgorithmException ex){
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_GET_MAC_ENGINE_INVALID_MAC_ALGORITHM.get(
                      keyEntry.getType(), getExceptionMessage(ex)),
              ex);
    }
    try {
      mac.init(keyEntry.getSecretKey());
    }
    catch (InvalidKeyException ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_GET_MAC_ENGINE_CANNOT_INITIALIZE.get(
                   getExceptionMessage(ex)), ex);
    }
    return mac;
  }
  /**
   * This method produces an initialized Cipher based on the supplied
   * CipherKeyEntry's state.
   *
   * @param keyEntry  The secret key entry containing the cipher
   * transformation and secret key for which to instantiate
   * the cipher.
   *
   * @param mode  Either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE.
   *
   * @param initializationVector  For Cipher.DECRYPT_MODE, supply
   * any initialzation vector used in the corresponding encryption
   * cipher. May be null.
   *
   * @return  The initialized cipher object.
   *
   * @throws  CryptoManagerException In case of a problem creating
   * or initializing the requested cipher object. Possible causes
   * include NoSuchAlgorithmException, NoSuchPaddingException,
   * InvalidKeyException, and InvalidAlgorithmParameterException.
   */
  private static Cipher getCipher(final CipherKeyEntry keyEntry,
                                  final int mode,
                                  final byte[] initializationVector)
          throws CryptoManagerException {
    Validator.ensureTrue(Cipher.ENCRYPT_MODE == mode
            || Cipher.DECRYPT_MODE == mode);
    Validator.ensureTrue(Cipher.ENCRYPT_MODE != mode
            || null == initializationVector);
    Validator.ensureTrue(-1 != keyEntry.getIVLengthBits()
            || Cipher.ENCRYPT_MODE == mode);
    Validator.ensureTrue(null == initializationVector
            || initializationVector.length * Byte.SIZE
                                       == keyEntry.getIVLengthBits());
    Cipher cipher;
    try {
      cipher = Cipher.getInstance(keyEntry.getType());
    }
    catch (GeneralSecurityException ex) {
      // NoSuchAlgorithmException, NoSuchPaddingException
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_GET_CIPHER_INVALID_CIPHER_TRANSFORMATION.get(
                   keyEntry.getType(), getExceptionMessage(ex)), ex);
    }
    try {
      if (0 < keyEntry.getIVLengthBits()) {
          byte[] iv;
          if (Cipher.ENCRYPT_MODE == mode
                  && null == initializationVector) {
            iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
            pseudoRandom.nextBytes(iv);
          }
          else {
            iv = initializationVector;
          }
          cipher.init(mode, keyEntry.getSecretKey(),
                  new IvParameterSpec(iv));
      }
      else {
        cipher.init(mode, keyEntry.getSecretKey());
      }
    }
    catch (GeneralSecurityException ex) {
      // InvalidKeyException, InvalidAlgorithmParameterException
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_GET_CIPHER_CANNOT_INITIALIZE.get(
                      getExceptionMessage(ex)), ex);
    }
    return cipher;
  }
  /**
   * Encrypts the data in the provided byte array using the preferred
   * cipher transformation.
   *
   * @param  data  The plain-text data to be encrypted.
   *
   * @return  A byte array containing the encrypted representation of
   *          the provided data.
   *
   * @throws  GeneralSecurityException  If a problem occurs while
   *          encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  public byte[] encrypt(byte[] data)
         throws GeneralSecurityException, CryptoManagerException
  {
    return encrypt(preferredCipherTransformation,
            preferredCipherTransformationKeyLengthBits, data);
  }
  /**
   * Encrypts the data in the provided byte array using the requested
   * cipher algorithm.
   *
   * @param  cipherTransformation  The algorithm/mode/padding to use
   *         for the cipher.
   *
   * @param  keyLengthBits  The length in bits of the encryption key
   *         this method is to use. Note the specified key length and
   *         transformation must be compatible.
   *
   * @param  data  The plain-text data to be encrypted.
   *
   * @return  A byte array containing the encrypted representation of
   *          the provided data.
   *
   * @throws  GeneralSecurityException  If a problem occurs while
   *          encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  public byte[] encrypt(String cipherTransformation,
                        int keyLengthBits,
                        byte[] data)
         throws GeneralSecurityException, CryptoManagerException
  {
    Validator.ensureNotNull(cipherTransformation, data);
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
            this, cipherTransformation, keyLengthBits);
    if (null == keyEntry) {
      keyEntry = CipherKeyEntry.generateKeyEntry(this,
              cipherTransformation, keyLengthBits);
    }
    final Cipher cipher
            = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
    final byte[] keyID = keyEntry.getKeyID().getByteValue();
    final byte[] iv = cipher.getIV();
    final int prologueLength
            = keyID.length + ((null == iv) ? 0 : iv.length);
    final int dataLength = cipher.getOutputSize(data.length);
    final byte[] cipherText = new byte[prologueLength + dataLength];
    System.arraycopy(keyID, 0, cipherText, 0, keyID.length);
    if (null != iv) {
      System.arraycopy(iv, 0, cipherText, keyID.length, iv.length);
    }
    System.arraycopy(cipher.doFinal(data), 0, cipherText,
                     prologueLength, dataLength);
    return cipherText;
  }
  /**
   * Writes encrypted data to the provided output stream using the
   * preferred cipher transformation.
   *
   * @param  outputStream The output stream to be wrapped by the
   *         returned cipher output stream.
   *
   * @return  The output stream wrapped with a CipherOutputStream.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  public CipherOutputStream getCipherOutputStream(
          OutputStream outputStream) throws CryptoManagerException
  {
    return getCipherOutputStream(preferredCipherTransformation,
            preferredCipherTransformationKeyLengthBits, outputStream);
  }
  /**
   * Writes encrypted data to the provided output stream using the
   * requested cipher transformation.
   *
   * @param  cipherTransformation  The algorithm/mode/padding to use
   *         for the cipher.
   *
   * @param  keyLengthBits  The length in bits of the encryption key
   *         this method will generate. Note the specified key length
   *         must be compatible with the transformation.
   *
   * @param  outputStream The output stream to be wrapped by the
   *         returned cipher output stream.
   *
   * @return  The output stream wrapped with a CipherOutputStream.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  public CipherOutputStream getCipherOutputStream(
          String cipherTransformation, int keyLengthBits,
          OutputStream outputStream)
         throws CryptoManagerException
  {
    Validator.ensureNotNull(cipherTransformation, outputStream);
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
            this, cipherTransformation, keyLengthBits);
    if (null == keyEntry) {
      keyEntry = CipherKeyEntry.generateKeyEntry(this,
              cipherTransformation, keyLengthBits);
    }
    final Cipher cipher
            = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
    final byte[] keyID = keyEntry.getKeyID().getByteValue();
    try {
      outputStream.write(keyID);
      if (null != cipher.getIV()) {
        outputStream.write(cipher.getIV());
      }
    }
    catch (IOException ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
             ERR_CRYPTOMGR_GET_CIPHER_STREAM_PROLOGUE_WRITE_ERROR.get(
                     getExceptionMessage(ex)), ex);
    }
    return new CipherOutputStream(outputStream, cipher);
  }
  /**
   * Decrypts the data in the provided byte array using cipher
   * specified by the key identifier prologue to the data.
   * cipher.
   *
   * @param  data  The cipher-text data to be decrypted.
   *
   * @return  A byte array containing the clear-text representation of
   *          the provided data.
   *
   * @throws  GeneralSecurityException  If a problem occurs while
   *          encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs reading the
   *          key identifier or initialization vector from the data
   *          prologue, or using these values to initialize a Cipher.
   */
  public byte[] decrypt(byte[] data)
         throws GeneralSecurityException,
                CryptoManagerException
  {
    KeyEntryID keyID;
    try {
      final byte[] keyIDBytes
              = new byte[KeyEntryID.getByteValueLength()];
      System.arraycopy(data, 0, keyIDBytes, 0, keyIDBytes.length);
      keyID = new KeyEntryID(keyIDBytes);
    }
    catch (Exception ex) {
      // IndexOutOfBoundsException, ArrayStoreException, ...
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get(),
              ex);
    }
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID);
    if (null == keyEntry) {
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get());
    }
    byte[] iv = null;
    if (0 < keyEntry.getIVLengthBits()) {
      iv = new byte[keyEntry.getIVLengthBits()/Byte.SIZE];
      try {
        System.arraycopy(data, KeyEntryID.getByteValueLength(), iv, 0,
                iv.length);
      }
      catch (Exception ex) {
        // IndexOutOfBoundsException, ArrayStoreException, ...
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        throw new CryptoManagerException(
               ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get(), ex);
      }
    }
    final Cipher cipher = getCipher(keyEntry, Cipher.DECRYPT_MODE,
            iv);
    final int prologueLength = KeyEntryID.getByteValueLength()
                                     + ((null == iv) ? 0 : iv.length);
    return cipher.doFinal(data, prologueLength,
                          data.length - prologueLength);
  }
  /**
   * Returns a CipherInputStream instantiated with a cipher
   * corresponding to the key identifier prologue to the data.
   *
   * @param  inputStream The input stream be wrapped with the
   *         CipherInputStream.
   *
   * @return The CiperInputStream instantiated as specified.
   *
   * @throws  CryptoManagerException If there is a problem reading the
   *          key ID or initialization vector from the input stream,
   *          or using these values to inititalize a Cipher.
   */
  public CipherInputStream getCipherInputStream(
          InputStream inputStream) throws CryptoManagerException
  {
    CipherKeyEntry keyEntry;
    byte[] iv = null;
    try {
      final byte[] keyID = new byte[KeyEntryID.getByteValueLength()];
      if (keyID.length != inputStream.read(keyID)){
        throw new CryptoManagerException(
           ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get());
      }
      keyEntry = CipherKeyEntry.getKeyEntry(this,
              new KeyEntryID(keyID));
      if (null == keyEntry) {
        throw new CryptoManagerException(
                ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get());
      }
      if (0 < keyEntry.getIVLengthBits()) {
        iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
        if (iv.length != inputStream.read(iv)) {
          throw new CryptoManagerException(
                  ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get());
        }
      }
    }
    catch (IOException ex) {
      throw new CryptoManagerException(
             ERR_CRYPTOMGR_DECRYPT_CIPHER_INPUT_STREAM_ERROR.get(
                     getExceptionMessage(ex)), ex);
    }
    return new CipherInputStream(inputStream,
            getCipher(keyEntry, Cipher.DECRYPT_MODE, iv));
  }
  /**
   * Attempts to compress the data in the provided source array into
   * the given destination array.  If the compressed data will fit
   * into the destination array, then this method will return the
   * number of bytes of compressed data in the array.  Otherwise, it
   * will return -1 to indicate that the compression was not
   * successful.  Note that if -1 is returned, then the data in the
   * destination array should be considered invalid.
   *
   * @param  src  The array containing the raw data to compress.
   * @param  dst  The array into which the compressed data should be
   *              written.
   *
   * @return  The number of bytes of compressed data, or -1 if it was
   *          not possible to actually compress the data.
   */
  public int compress(byte[] src, byte[] dst)
  {
    Deflater deflater = new Deflater();
    try
    {
      deflater.setInput(src);
      deflater.finish();
      int compressedLength = deflater.deflate(dst);
      if (deflater.finished())
      {
        return compressedLength;
      }
      else
      {
        return -1;
      }
    }
    finally
    {
      deflater.end();
    }
  }
  /**
   * Attempts to uncompress the data in the provided source array into
   * the given destination array.  If the uncompressed data will fit
   * into the given destination array, then this method will return
   * the number of bytes of uncompressed data written into the
   * destination buffer.  Otherwise, it will return a negative value
   * to indicate that the destination buffer was not large enough.
   * The absolute value of that negative return value will indicate
   * the buffer size required to fully decompress the data.  Note that
   * if a negative value is returned, then the data in the destination
   * array should be considered invalid.
   *
   * @param  src  The array containing the compressed data.
   * @param  dst  The array into which the uncompressed data should be
   *              written.
   *
   * @return  A positive value containing the number of bytes of
   *          uncompressed data written into the destination buffer,
   *          or a negative value whose absolute value is the size of
   *          the destination buffer required to fully decompress the
   *          provided data.
   *
   * @throws  DataFormatException  If a problem occurs while
   *                               attempting to uncompress the data.
   */
  public int uncompress(byte[] src, byte[] dst)
         throws DataFormatException
  {
    Inflater inflater = new Inflater();
    try
    {
      inflater.setInput(src);
      int decompressedLength = inflater.inflate(dst);
      if (inflater.finished())
      {
        return decompressedLength;
      }
      else
      {
        int totalLength = decompressedLength;
        while (! inflater.finished())
        LinkedList<SearchResultEntry> resultEntries =
             internalSearch.getSearchEntries();
        for (SearchResultEntry resultEntry : resultEntries)
        {
          totalLength += inflater.inflate(dst);
          AttributeType hostnameAttr =
               DirectoryServer.getAttributeType("hostname", true);
          String hostname = resultEntry.getAttributeValue(
               hostnameAttr, DirectoryStringSyntax.DECODER);
          AttributeType ldapPortAttr =
               DirectoryServer.getAttributeType("ldapport", true);
          Integer ldapPort = resultEntry.getAttributeValue(
               ldapPortAttr, IntegerSyntax.DECODER);
          // Connect to the server.
          AtomicInteger nextMessageID = new AtomicInteger(1);
          LDAPConnectionOptions connectionOptions =
               new LDAPConnectionOptions();
          PrintStream nullPrintStream =
               new PrintStream(new OutputStream() {
                 public void write ( int b ) { }
               });
          LDAPConnection connection =
               new LDAPConnection(hostname, ldapPort,
                                  connectionOptions,
                                  nullPrintStream,
                                  nullPrintStream);
          connection.connectToHost(null, null, nextMessageID);
          try
          {
            LDAPReader reader = connection.getLDAPReader();
            LDAPWriter writer = connection.getLDAPWriter();
            // Send the Get Symmetric Key extended request.
            ASN1OctetString requestValue =
                 GetSymmetricKeyExtendedOperation.encodeRequestValue(
                      symmetricKey, getInstanceKeyID());
            ExtendedRequestProtocolOp extendedRequest =
                 new ExtendedRequestProtocolOp(
                      ServerConstants.
                           OID_GET_SYMMETRIC_KEY_EXTENDED_OP,
                      requestValue);
            ArrayList<LDAPControl> controls =
                 new ArrayList<LDAPControl>();
            LDAPMessage requestMessage =
                 new LDAPMessage(nextMessageID.getAndIncrement(),
                                 extendedRequest, controls);
            writer.writeMessage(requestMessage);
            LDAPMessage responseMessage = reader.readMessage();
            ExtendedResponseProtocolOp extendedResponse =
                 responseMessage.getExtendedResponseProtocolOp();
            if (extendedResponse.getResultCode() ==
                 LDAPResultCode.SUCCESS)
            {
              // Got our symmetric key value.
              return extendedResponse.getValue().stringValue();
            }
          }
          finally
          {
            connection.close(nextMessageID);
          }
        }
        return -totalLength;
      }
    }
    finally
    {
      inflater.end();
    }
  }
  /**
   * Retrieve the ADS trust store backend.
   * @return The ADS trust store backend.
   * @throws ConfigException If the ADS trust store backend is
   *                         not configured.
   */
  private TrustStoreBackend getTrustStoreBackend()
       throws ConfigException
  {
    Backend b = DirectoryServer.getBackend(
         ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
    if (b == null)
    {
      Message msg =
           ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_NOT_ENABLED.get(
                ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
      throw new ConfigException(msg);
    }
    if (!(b instanceof TrustStoreBackend))
    {
      Message msg =
           ERR_CRYPTOMGR_ADS_TRUST_STORE_BACKEND_WRONG_CLASS.get(
                ConfigConstants.ID_ADS_TRUST_STORE_BACKEND);
      throw new ConfigException(msg);
    }
    return (TrustStoreBackend)b;
  }
  /**
   * Create an SSL context that may be used for communication to
   * another ADS component.
   *
   * @param sslCertNickname The name of the local certificate to use,
   *                        or null if none is specified.
   * @return A new SSL Context.
   * @throws ConfigException If the context could not be created.
   */
  public SSLContext getSslContext(String sslCertNickname)
       throws ConfigException
  {
    SSLContext sslContext;
    try
    {
      TrustStoreBackend trustStoreBackend = getTrustStoreBackend();
      KeyManager[] keyManagers = trustStoreBackend.getKeyManagers();
      TrustManager[] trustManagers =
           trustStoreBackend.getTrustManagers();
      sslContext = SSLContext.getInstance("TLS");
      if (sslCertNickname == null)
      catch (Exception e)
      {
        sslContext.init(keyManagers, trustManagers, null);
      }
      else
      {
        X509ExtendedKeyManager[] extendedKeyManagers =
             SelectableCertificateKeyManager.wrap(
                  keyManagers,
                  sslCertNickname);
        sslContext.init(extendedKeyManagers, trustManagers, null);
        // Just try another server.
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
           ERR_CRYPTOMGR_SSL_CONTEXT_CANNOT_INITIALIZE.get(
                getExceptionMessage(e));
      throw new ConfigException(message, e);
    }
    return sslContext;
    // Give up.
    return null;
  }
  /**
   * Get the name of the local certificate to use for SSL.
   * @return The name of the local certificate to use for SSL.
   */
  public String getSslCertNickname()
  {
    return sslCertNickname;
  }
  /**
   * Determine whether SSL encryption is enabled.
   * @return true if SSL encryption is enabled.
   */
  public boolean isSslEncryption()
  {
    return sslEncryption;
  }
  /**
   * Get the set of enabled SSL protocols.
   * @return The set of enabled SSL protocols.
   */
  public SortedSet<String> getSslProtocols()
  {
    return sslProtocols;
  }
  /**
   * Get the set of enabled SSL cipher suites.
   * @return The set of enabled SSL cipher suites.
   */
  public SortedSet<String> getSslCipherSuites()
  {
    return sslCipherSuites;
  }
  /**
   * Imports a cipher key entry from an entry in ADS.
   *
   * @param entry  The ADS cipher key entry to be imported.
@@ -1999,119 +1266,6 @@
    }
  }
  /**
   * Given a set of other servers' symmetric key values for
   * a given secret key, use the Get Symmetric Key extended
   * operation to request this server's symmetric key value.
   *
   * @param  symmetricKeys  The known symmetric key values for
   *                        a given secret key.
   *
   * @return The symmetric key value for this server, or null if
   *         none could be obtained.
   */
  private String getSymmetricKey(List<String> symmetricKeys)
  {
    InternalClientConnection internalConnection =
         InternalClientConnection.getRootConnection();
    for (String symmetricKey : symmetricKeys)
    {
      try
      {
        // Get the server instance key ID from the symmetric key.
        String[] elements = symmetricKey.split(":", 0);
        String instanceKeyID = elements[0];
        // Find the server entry from the instance key ID.
        String filter = "(" +
             ConfigConstants.ATTR_CRYPTO_KEY_ID + "=" +
             instanceKeyID + ")";
        InternalSearchOperation internalSearch =
             internalConnection.processSearch(
                  serversDN, SearchScope.SUBORDINATE_SUBTREE,
                  SearchFilter.createFilterFromString(filter));
        if (internalSearch.getResultCode() != ResultCode.SUCCESS)
          continue;
        LinkedList<SearchResultEntry> resultEntries =
             internalSearch.getSearchEntries();
        for (SearchResultEntry resultEntry : resultEntries)
        {
          AttributeType hostnameAttr =
               DirectoryServer.getAttributeType("hostname", true);
          String hostname = resultEntry.getAttributeValue(
               hostnameAttr, DirectoryStringSyntax.DECODER);
          AttributeType ldapPortAttr =
               DirectoryServer.getAttributeType("ldapport", true);
          Integer ldapPort = resultEntry.getAttributeValue(
               ldapPortAttr, IntegerSyntax.DECODER);
          // Connect to the server.
          AtomicInteger nextMessageID = new AtomicInteger(1);
          LDAPConnectionOptions connectionOptions =
               new LDAPConnectionOptions();
          PrintStream nullPrintStream =
               new PrintStream(new OutputStream() {
                 public void write ( int b ) { }
               });
          LDAPConnection connection =
               new LDAPConnection(hostname, ldapPort,
                                  connectionOptions,
                                  nullPrintStream,
                                  nullPrintStream);
          connection.connectToHost(null, null, nextMessageID);
          try
          {
            LDAPReader reader = connection.getLDAPReader();
            LDAPWriter writer = connection.getLDAPWriter();
            // Send the Get Symmetric Key extended request.
            ASN1OctetString requestValue =
                 GetSymmetricKeyExtendedOperation.encodeRequestValue(
                      symmetricKey, getInstanceKeyID());
            ExtendedRequestProtocolOp extendedRequest =
                 new ExtendedRequestProtocolOp(
                      ServerConstants.
                           OID_GET_SYMMETRIC_KEY_EXTENDED_OP,
                      requestValue);
            ArrayList<LDAPControl> controls =
                 new ArrayList<LDAPControl>();
            LDAPMessage requestMessage =
                 new LDAPMessage(nextMessageID.getAndIncrement(),
                                 extendedRequest, controls);
            writer.writeMessage(requestMessage);
            LDAPMessage responseMessage = reader.readMessage();
            ExtendedResponseProtocolOp extendedResponse =
                 responseMessage.getExtendedResponseProtocolOp();
            if (extendedResponse.getResultCode() ==
                 LDAPResultCode.SUCCESS)
            {
              // Got our symmetric key value.
              return extendedResponse.getValue().stringValue();
            }
          }
          finally
          {
            connection.close(nextMessageID);
          }
        }
      }
      catch (Exception e)
      {
        // Just try another server.
      }
    }
    // Give up.
    return null;
  }
  /**
   * Imports a mac key entry from an entry in ADS.
@@ -2219,6 +1373,7 @@
    }
  }
  /**
   * This class implements a utility interface to the unique
   * identifier corresponding to a cryptographic key. For each key
@@ -2517,10 +1672,10 @@
     * instantiating a Cipher object in order to validate the supplied
     * parameters when creating a new entry.
     *
     * @see CipherKeyEntry#getKeyEntry(CryptoManager, String, int)
     * @see CipherKeyEntry#getKeyEntry(CryptoManagerImpl, String, int)
     */
    public static CipherKeyEntry generateKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String transformation,
            final int keyLengthBits)
    throws CryptoManagerException {
@@ -2563,7 +1718,7 @@
     *                       If the key entry could not be added to
     *                       ADS.
     */
    private static void publishKeyEntry(CryptoManager cryptoManager,
    private static void publishKeyEntry(CryptoManagerImpl cryptoManager,
                                        CipherKeyEntry keyEntry)
         throws CryptoManagerException
    {
@@ -2644,7 +1799,7 @@
      // Need to add our own instance certificate.
      byte[] instanceKeyCertificate =
         CryptoManager.getInstanceKeyCertificateFromLocalTruststore();
         CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore();
      trustedCerts.put(getInstanceKeyID(instanceKeyCertificate),
                       instanceKeyCertificate);
@@ -2720,7 +1875,7 @@
     * parameters used to initialize or validate the key entry.
     */
    public static CipherKeyEntry importCipherKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String keyIDString,
            final String transformation,
            final SecretKey secretKey,
@@ -2791,7 +1946,7 @@
     * {@code null} if no such entry exists.
     */
    public static CipherKeyEntry getKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String transformation,
            final int keyLengthBits) {
      Validator.ensureNotNull(cryptoManager, transformation);
@@ -2838,12 +1993,12 @@
     * @return  The key entry associated with the key identifier, or
     * {@code null} if no such entry exists.
     *
     * @see org.opends.server.crypto.CryptoManager.MacKeyEntry
     * @see CryptoManagerImpl.MacKeyEntry
     *  #getKeyEntry(org.opends.server.types.CryptoManager,
     *               java.lang.String, int)
     */
    public static CipherKeyEntry getKeyEntry(
            CryptoManager cryptoManager,
            CryptoManagerImpl cryptoManager,
            final KeyEntryID keyID) {
      return cryptoManager.cipherKeyEntryCache.get(keyID);
    }
@@ -2983,6 +2138,86 @@
  }
  /**
   * This method produces an initialized Cipher based on the supplied
   * CipherKeyEntry's state.
   *
   * @param keyEntry  The secret key entry containing the cipher
   * transformation and secret key for which to instantiate
   * the cipher.
   *
   * @param mode  Either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE.
   *
   * @param initializationVector  For Cipher.DECRYPT_MODE, supply
   * any initialzation vector used in the corresponding encryption
   * cipher. May be null.
   *
   * @return  The initialized cipher object.
   *
   * @throws  CryptoManagerException In case of a problem creating
   * or initializing the requested cipher object. Possible causes
   * include NoSuchAlgorithmException, NoSuchPaddingException,
   * InvalidKeyException, and InvalidAlgorithmParameterException.
   */
  private static Cipher getCipher(final CipherKeyEntry keyEntry,
                                  final int mode,
                                  final byte[] initializationVector)
          throws CryptoManagerException {
    Validator.ensureTrue(Cipher.ENCRYPT_MODE == mode
            || Cipher.DECRYPT_MODE == mode);
    Validator.ensureTrue(Cipher.ENCRYPT_MODE != mode
            || null == initializationVector);
    Validator.ensureTrue(-1 != keyEntry.getIVLengthBits()
            || Cipher.ENCRYPT_MODE == mode);
    Validator.ensureTrue(null == initializationVector
            || initializationVector.length * Byte.SIZE
                                       == keyEntry.getIVLengthBits());
    Cipher cipher;
    try {
      cipher = Cipher.getInstance(keyEntry.getType());
    }
    catch (GeneralSecurityException ex) {
      // NoSuchAlgorithmException, NoSuchPaddingException
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_GET_CIPHER_INVALID_CIPHER_TRANSFORMATION.get(
                   keyEntry.getType(), getExceptionMessage(ex)), ex);
    }
    try {
      if (0 < keyEntry.getIVLengthBits()) {
          byte[] iv;
          if (Cipher.ENCRYPT_MODE == mode
                  && null == initializationVector) {
            iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
            pseudoRandom.nextBytes(iv);
          }
          else {
            iv = initializationVector;
          }
          cipher.init(mode, keyEntry.getSecretKey(),
                  new IvParameterSpec(iv));
      }
      else {
        cipher.init(mode, keyEntry.getSecretKey());
      }
    }
    catch (GeneralSecurityException ex) {
      // InvalidKeyException, InvalidAlgorithmParameterException
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_GET_CIPHER_CANNOT_INITIALIZE.get(
                      getExceptionMessage(ex)), ex);
    }
    return cipher;
  }
  /**
   * This class corresponds to the MAC key entry in ADS. It is
@@ -3014,10 +2249,10 @@
     * instantiating a Mac object in order to validate the supplied
     * parameters when creating a new entry.
     *
     * @see MacKeyEntry#getKeyEntry(CryptoManager, String, int)
     * @see MacKeyEntry#getKeyEntry(CryptoManagerImpl, String, int)
     */
    public static MacKeyEntry generateKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String algorithm,
            final int keyLengthBits)
    throws CryptoManagerException {
@@ -3056,7 +2291,7 @@
     *                       If the key entry could not be added to
     *                       ADS.
     */
    private static void publishKeyEntry(CryptoManager cryptoManager,
    private static void publishKeyEntry(CryptoManagerImpl cryptoManager,
                                        MacKeyEntry keyEntry)
         throws CryptoManagerException
    {
@@ -3123,7 +2358,7 @@
      // Need to add our own instance certificate.
      byte[] instanceKeyCertificate =
         CryptoManager.getInstanceKeyCertificateFromLocalTruststore();
         CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore();
      trustedCerts.put(getInstanceKeyID(instanceKeyCertificate),
                       instanceKeyCertificate);
@@ -3193,7 +2428,7 @@
     * parameters used to initialize or validate the key entry.
     */
    public static MacKeyEntry importMacKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String keyIDString,
            final String algorithm,
            final SecretKey secretKey,
@@ -3257,7 +2492,7 @@
     * {@code null} if no such entry exists.
     */
    public static MacKeyEntry getKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final String algorithm,
            final int keyLengthBits) {
      Validator.ensureNotNull(cryptoManager, algorithm);
@@ -3304,12 +2539,12 @@
     * @return  The key entry associated with the key identifier, or
     * {@code null} if no such entry exists.
     *
     * @see org.opends.server.crypto.CryptoManager.CipherKeyEntry
     * @see CryptoManagerImpl.CipherKeyEntry
     *     #getKeyEntry(org.opends.server.types.CryptoManager,
     *                  java.lang.String, int)
     */
    public static MacKeyEntry getKeyEntry(
            final CryptoManager cryptoManager,
            final CryptoManagerImpl cryptoManager,
            final KeyEntryID keyID) {
      return cryptoManager.macKeyEntryCache.get(keyID);
    }
@@ -3379,8 +2614,482 @@
      return fType;
    }
    // state
    private final String fType;
  }
  /**
   * This method produces an initialized MAC engine based on the
   * supplied MacKeyEntry's state.
   *
   * @param keyEntry The MacKeyEntry specifying the Mac properties.
   *
   * @return  An initialized Mac object.
   *
   * @throws CryptoManagerException  In case there was a error
   * instantiating the Mac object.
   */
  private static Mac getMacEngine(MacKeyEntry keyEntry)
          throws CryptoManagerException
  {
    Mac mac;
    try {
      mac = Mac.getInstance(keyEntry.getType());
    }
    catch (NoSuchAlgorithmException ex){
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_GET_MAC_ENGINE_INVALID_MAC_ALGORITHM.get(
                      keyEntry.getType(), getExceptionMessage(ex)),
              ex);
    }
    try {
      mac.init(keyEntry.getSecretKey());
    }
    catch (InvalidKeyException ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_GET_MAC_ENGINE_CANNOT_INITIALIZE.get(
                   getExceptionMessage(ex)), ex);
    }
    return mac;
  }
  /** {@inheritDoc} */
  public String getPreferredMessageDigestAlgorithm()
  {
    return preferredDigestAlgorithm;
  }
  /** {@inheritDoc} */
  public MessageDigest getPreferredMessageDigest()
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(preferredDigestAlgorithm);
  }
  /** {@inheritDoc} */
  public MessageDigest getMessageDigest(String digestAlgorithm)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(digestAlgorithm);
  }
  /** {@inheritDoc} */
  public byte[] digest(byte[] data)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(preferredDigestAlgorithm).
                digest(data);
  }
  /** {@inheritDoc} */
  public byte[] digest(String digestAlgorithm, byte[] data)
         throws NoSuchAlgorithmException
  {
    return MessageDigest.getInstance(digestAlgorithm).digest(data);
  }
  /** {@inheritDoc} */
  public byte[] digest(InputStream inputStream)
         throws IOException, NoSuchAlgorithmException
  {
    MessageDigest digest =
         MessageDigest.getInstance(preferredDigestAlgorithm);
    byte[] buffer = new byte[8192];
    while (true)
    {
      int bytesRead = inputStream.read(buffer);
      if (bytesRead < 0)
      {
        break;
      }
      digest.update(buffer, 0, bytesRead);
    }
    return digest.digest();
  }
  /** {@inheritDoc} */
  public byte[] digest(String digestAlgorithm,
                       InputStream inputStream)
         throws IOException, NoSuchAlgorithmException
  {
    MessageDigest digest = MessageDigest.getInstance(digestAlgorithm);
    byte[] buffer = new byte[8192];
    while (true)
    {
      int bytesRead = inputStream.read(buffer);
      if (bytesRead < 0)
      {
        break;
      }
      digest.update(buffer, 0, bytesRead);
    }
    return digest.digest();
  }
  /** {@inheritDoc} */
  public String getMacEngineKeyEntryID()
          throws CryptoManagerException
  {
    return getMacEngineKeyEntryID(preferredMACAlgorithm,
            preferredMACAlgorithmKeyLengthBits);
  }
  /** {@inheritDoc} */
  public String getMacEngineKeyEntryID(final String macAlgorithm,
                                       final int keyLengthBits)
         throws CryptoManagerException {
    Validator.ensureNotNull(macAlgorithm);
    MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this, macAlgorithm,
                                                   keyLengthBits);
    if (null == keyEntry) {
      keyEntry = MacKeyEntry.generateKeyEntry(this, macAlgorithm,
                                              keyLengthBits);
    }
    return keyEntry.getKeyID().getStringValue();
  }
  /** {@inheritDoc} */
  public Mac getMacEngine(String keyEntryID)
          throws CryptoManagerException
  {
    final MacKeyEntry keyEntry = MacKeyEntry.getKeyEntry(this,
            new KeyEntryID(keyEntryID));
    return (null == keyEntry) ? null : getMacEngine(keyEntry);
  }
  /** {@inheritDoc} */
  public byte[] encrypt(byte[] data)
         throws GeneralSecurityException, CryptoManagerException
  {
    return encrypt(preferredCipherTransformation,
            preferredCipherTransformationKeyLengthBits, data);
  }
  /** {@inheritDoc} */
  public byte[] encrypt(String cipherTransformation,
                        int keyLengthBits,
                        byte[] data)
         throws GeneralSecurityException, CryptoManagerException
  {
    Validator.ensureNotNull(cipherTransformation, data);
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
            this, cipherTransformation, keyLengthBits);
    if (null == keyEntry) {
      keyEntry = CipherKeyEntry.generateKeyEntry(this,
              cipherTransformation, keyLengthBits);
    }
    final Cipher cipher
            = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
    final byte[] keyID = keyEntry.getKeyID().getByteValue();
    final byte[] iv = cipher.getIV();
    final int prologueLength
            = keyID.length + ((null == iv) ? 0 : iv.length);
    final int dataLength = cipher.getOutputSize(data.length);
    final byte[] cipherText = new byte[prologueLength + dataLength];
    System.arraycopy(keyID, 0, cipherText, 0, keyID.length);
    if (null != iv) {
      System.arraycopy(iv, 0, cipherText, keyID.length, iv.length);
    }
    System.arraycopy(cipher.doFinal(data), 0, cipherText,
                     prologueLength, dataLength);
    return cipherText;
  }
  /** {@inheritDoc} */
  public CipherOutputStream getCipherOutputStream(
          OutputStream outputStream) throws CryptoManagerException
  {
    return getCipherOutputStream(preferredCipherTransformation,
            preferredCipherTransformationKeyLengthBits, outputStream);
  }
  /** {@inheritDoc} */
  public CipherOutputStream getCipherOutputStream(
          String cipherTransformation, int keyLengthBits,
          OutputStream outputStream)
         throws CryptoManagerException
  {
    Validator.ensureNotNull(cipherTransformation, outputStream);
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(
            this, cipherTransformation, keyLengthBits);
    if (null == keyEntry) {
      keyEntry = CipherKeyEntry.generateKeyEntry(this,
              cipherTransformation, keyLengthBits);
    }
    final Cipher cipher
            = getCipher(keyEntry, Cipher.ENCRYPT_MODE, null);
    final byte[] keyID = keyEntry.getKeyID().getByteValue();
    try {
      outputStream.write(keyID);
      if (null != cipher.getIV()) {
        outputStream.write(cipher.getIV());
      }
    }
    catch (IOException ex) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
             ERR_CRYPTOMGR_GET_CIPHER_STREAM_PROLOGUE_WRITE_ERROR.get(
                     getExceptionMessage(ex)), ex);
    }
    return new CipherOutputStream(outputStream, cipher);
  }
  /** {@inheritDoc} */
  public byte[] decrypt(byte[] data)
         throws GeneralSecurityException,
                CryptoManagerException
  {
    KeyEntryID keyID;
    try {
      final byte[] keyIDBytes
              = new byte[KeyEntryID.getByteValueLength()];
      System.arraycopy(data, 0, keyIDBytes, 0, keyIDBytes.length);
      keyID = new KeyEntryID(keyIDBytes);
    }
    catch (Exception ex) {
      // IndexOutOfBoundsException, ArrayStoreException, ...
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, ex);
      }
      throw new CryptoManagerException(
           ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get(),
              ex);
    }
    CipherKeyEntry keyEntry = CipherKeyEntry.getKeyEntry(this, keyID);
    if (null == keyEntry) {
      throw new CryptoManagerException(
              ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get());
    }
    byte[] iv = null;
    if (0 < keyEntry.getIVLengthBits()) {
      iv = new byte[keyEntry.getIVLengthBits()/Byte.SIZE];
      try {
        System.arraycopy(data, KeyEntryID.getByteValueLength(), iv, 0,
                iv.length);
      }
      catch (Exception ex) {
        // IndexOutOfBoundsException, ArrayStoreException, ...
        if (debugEnabled()) {
          TRACER.debugCaught(DebugLogLevel.ERROR, ex);
        }
        throw new CryptoManagerException(
               ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get(), ex);
      }
    }
    final Cipher cipher = getCipher(keyEntry, Cipher.DECRYPT_MODE,
            iv);
    final int prologueLength = KeyEntryID.getByteValueLength()
                                     + ((null == iv) ? 0 : iv.length);
    return cipher.doFinal(data, prologueLength,
                          data.length - prologueLength);
  }
 /** {@inheritDoc} */
  public CipherInputStream getCipherInputStream(
          InputStream inputStream) throws CryptoManagerException
  {
    CipherKeyEntry keyEntry;
    byte[] iv = null;
    try {
      final byte[] keyID = new byte[KeyEntryID.getByteValueLength()];
      if (keyID.length != inputStream.read(keyID)){
        throw new CryptoManagerException(
           ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_KEY_IDENTIFIER.get());
      }
      keyEntry = CipherKeyEntry.getKeyEntry(this,
              new KeyEntryID(keyID));
      if (null == keyEntry) {
        throw new CryptoManagerException(
                ERR_CRYPTOMGR_DECRYPT_UNKNOWN_KEY_IDENTIFIER.get());
      }
      if (0 < keyEntry.getIVLengthBits()) {
        iv = new byte[keyEntry.getIVLengthBits() / Byte.SIZE];
        if (iv.length != inputStream.read(iv)) {
          throw new CryptoManagerException(
                  ERR_CRYPTOMGR_DECRYPT_FAILED_TO_READ_IV.get());
        }
      }
    }
    catch (IOException ex) {
      throw new CryptoManagerException(
             ERR_CRYPTOMGR_DECRYPT_CIPHER_INPUT_STREAM_ERROR.get(
                     getExceptionMessage(ex)), ex);
    }
    return new CipherInputStream(inputStream,
            getCipher(keyEntry, Cipher.DECRYPT_MODE, iv));
  }
  /** {@inheritDoc} */
  public int compress(byte[] src, byte[] dst)
  {
    Deflater deflater = new Deflater();
    try
    {
      deflater.setInput(src);
      deflater.finish();
      int compressedLength = deflater.deflate(dst);
      if (deflater.finished())
      {
        return compressedLength;
      }
      else
      {
        return -1;
      }
    }
    finally
    {
      deflater.end();
    }
  }
  /** {@inheritDoc} */
  public int uncompress(byte[] src, byte[] dst)
         throws DataFormatException
  {
    Inflater inflater = new Inflater();
    try
    {
      inflater.setInput(src);
      int decompressedLength = inflater.inflate(dst);
      if (inflater.finished())
      {
        return decompressedLength;
      }
      else
      {
        int totalLength = decompressedLength;
        while (! inflater.finished())
        {
          totalLength += inflater.inflate(dst);
        }
        return -totalLength;
      }
    }
    finally
    {
      inflater.end();
    }
  }
  /** {@inheritDoc} */
  public SSLContext getSslContext(String sslCertNickname)
       throws ConfigException
  {
    SSLContext sslContext;
    try
    {
      TrustStoreBackend trustStoreBackend = getTrustStoreBackend();
      KeyManager[] keyManagers = trustStoreBackend.getKeyManagers();
      TrustManager[] trustManagers =
           trustStoreBackend.getTrustManagers();
      sslContext = SSLContext.getInstance("TLS");
      if (sslCertNickname == null)
      {
        sslContext.init(keyManagers, trustManagers, null);
      }
      else
      {
        X509ExtendedKeyManager[] extendedKeyManagers =
             SelectableCertificateKeyManager.wrap(
                  keyManagers,
                  sslCertNickname);
        sslContext.init(extendedKeyManagers, trustManagers, null);
      }
    }
    catch (Exception e)
    {
      if (debugEnabled())
      {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }
      Message message =
           ERR_CRYPTOMGR_SSL_CONTEXT_CANNOT_INITIALIZE.get(
                getExceptionMessage(e));
      throw new ConfigException(message, e);
    }
    return sslContext;
  }
  /** {@inheritDoc} */
  public String getSslCertNickname()
  {
    return sslCertNickname;
  }
  /** {@inheritDoc} */
  public boolean isSslEncryption()
  {
    return sslEncryption;
  }
  /** {@inheritDoc} */
  public SortedSet<String> getSslProtocols()
  {
    return sslProtocols;
  }
  /** {@inheritDoc} */
  public SortedSet<String> getSslCipherSuites()
  {
    return sslCipherSuites;
  }
}
opends/src/server/org/opends/server/crypto/CryptoManagerSync.java
@@ -123,7 +123,7 @@
          throws InitializationException
  {
    try {
      CryptoManager.publishInstanceKeyEntryInADS();
      CryptoManagerImpl.publishInstanceKeyEntryInADS();
    }
    catch (CryptoManagerException ex) {
      throw new InitializationException(ex.getMessageObject());
opends/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperation.java
@@ -218,7 +218,7 @@
      return;
    }
    CryptoManager cm = DirectoryServer.getCryptoManager();
    CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
    try
    {
      String responseSymmetricKey = cm.reencodeSymmetricKeyAttribute(
opends/src/server/org/opends/server/extensions/AESPasswordStorageScheme.java
@@ -36,13 +36,7 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import static org.opends.messages.ExtensionMessages.*;
opends/src/server/org/opends/server/extensions/BlowfishPasswordStorageScheme.java
@@ -36,13 +36,7 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import static org.opends.messages.ExtensionMessages.*;
opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -76,30 +76,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.schema.GeneralizedTimeSyntax;
import org.opends.server.tools.LDIFModify;
import org.opends.server.types.AttributeType;
import org.opends.server.types.BackupConfig;
import org.opends.server.types.BackupDirectory;
import org.opends.server.types.BackupInfo;
import org.opends.server.types.ConditionResult;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryEnvironmentConfig;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.IndexType;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFImportResult;
import org.opends.server.types.Modification;
import org.opends.server.types.Privilege;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.*;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFReader;
opends/src/server/org/opends/server/extensions/RC4PasswordStorageScheme.java
@@ -36,13 +36,7 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import static org.opends.messages.ExtensionMessages.*;
opends/src/server/org/opends/server/extensions/TripleDESPasswordStorageScheme.java
@@ -36,13 +36,7 @@
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.loggers.debug.DebugTracer;
import org.opends.server.types.ByteString;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.DebugLogLevel;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.*;
import org.opends.server.util.Base64;
import static org.opends.messages.ExtensionMessages.*;
opends/src/server/org/opends/server/replication/protocol/ReplSessionSecurity.java
@@ -30,7 +30,7 @@
import org.opends.server.admin.std.server.ReplicationServerCfg;
import org.opends.server.admin.std.server.ReplicationDomainCfg;
import org.opends.server.types.DirectoryConfig;
import org.opends.server.crypto.CryptoManager;
import org.opends.server.types.CryptoManager;
import org.opends.server.config.ConfigException;
import javax.net.ssl.SSLSocket;
opends/src/server/org/opends/server/types/CryptoManager.java
New file
@@ -0,0 +1,423 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import org.opends.server.config.ConfigException;
import javax.crypto.Mac;
import javax.crypto.CipherOutputStream;
import javax.crypto.CipherInputStream;
import javax.net.ssl.SSLContext;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.GeneralSecurityException;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.DataFormatException;
import java.util.SortedSet;
/**
 This interface defines the methods to call to access cryptographic
 services including encryption and hashing; in particular, when the
 ciphertext or HMAC is produced on one directory server instance and
 is to be consumed on another.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)public interface CryptoManager {
  /**
   * Retrieves the name of the preferred message digest algorithm.
   *
   * @return  The name of the preferred message digest algorithm
   */
  String getPreferredMessageDigestAlgorithm();
  /**
   * Retrieves a <CODE>MessageDigest</CODE> object that may be used to
   * generate digests using the preferred digest algorithm.
   *
   * @return  A <CODE>MessageDigest</CODE> object that may be used to
   *          generate digests using the preferred digest algorithm.
   *
   * @throws java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  MessageDigest getPreferredMessageDigest()
         throws NoSuchAlgorithmException;
  /**
   * Retrieves a <CODE>MessageDigest</CODE> object that may be used to
   * generate digests using the specified algorithm.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   *
   * @return  A <CODE>MessageDigest</CODE> object that may be used to
   *          generate digests using the specified algorithm.
   *
   * @throws  java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  MessageDigest getMessageDigest(String digestAlgorithm)
         throws NoSuchAlgorithmException;
  /**
   * Retrieves a byte array containing a message digest based on the
   * provided data, using the preferred digest algorithm.
   *
   * @param  data  The data to be digested.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  byte[] digest(byte[] data)
         throws NoSuchAlgorithmException;
  /**
   * Retrieves a byte array containing a message digest based on the
   * provided data, using the requested digest algorithm.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   * @param  data             The data to be digested.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  byte[] digest(String digestAlgorithm, byte[] data)
         throws NoSuchAlgorithmException;
  /**
   * Retrieves a byte array containing a message digest based on the
   * data read from the provided input stream, using the preferred
   * digest algorithm.  Data will be read until the end of the stream
   * is reached.
   *
   * @param  inputStream  The input stream from which the data is to
   *                      be read.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws java.io.IOException  If a problem occurs while reading
   * data from the provided stream.
   *
   * @throws  java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  byte[] digest(InputStream inputStream)
         throws IOException, NoSuchAlgorithmException;
  /**
   * Retrieves a byte array containing a message digest based on the
   * data read from the provided input stream, using the requested
   * digest algorithm.  Data will be read until the end of the stream
   * is reached.
   *
   * @param  digestAlgorithm  The algorithm to use to generate the
   *                          message digest.
   * @param  inputStream      The input stream from which the data is
   *                          to be read.
   *
   * @return  A byte array containing the generated message digest.
   *
   * @throws  java.io.IOException  If a problem occurs while reading
   * data from the provided stream.
   *
   * @throws  java.security.NoSuchAlgorithmException  If the requested
   * algorithm is not supported or is unavailable.
   */
  byte[] digest(String digestAlgorithm,
                       InputStream inputStream)
         throws IOException, NoSuchAlgorithmException;
  /**
   * For the current preferred MAC algorithm and key length, return
   * the identifier of the corresponding key entry. Note: the result
   * (key identifier) might change across invocations, due to either
   * of the perferred parameters changing, or because the original
   * key was marked compromised and a replacement key generated.
   *
   * @return A String representation of the identifier of a key entry
   * corresponding to the preferred MAC algorithm and key length.
   *
   * @throws CryptoManagerException In case one or more of the key
   * parameters is invalid, or there is a problem instantiating the
   * key entry in case it does not already exist.
   */
  String getMacEngineKeyEntryID()
          throws CryptoManagerException;
  /**
   * For the specified MAC algorithm and key length, return
   * the identifier of the corresponding key entry. Note: the result
   * (key identifier) might change across invocations, due to either
   * of the perferred parameters changing, or because the original
   * key was marked compromised and a replacement key generated.
   *
   * @param  macAlgorithm  The algorithm to use for the MAC engine.
   *
   * @param  keyLengthBits  The key length in bits to use with the
   *         specified algorithm.
   *
   * @return A String representation of the identifier of a key entry
   * corresponding to the specified MAC algorithm and key length.
   *
   * @throws CryptoManagerException In case one or more of the key
   * parameters is invalid, or there is a problem instantiating the
   * key entry in case it does not already exist.
   */
  String getMacEngineKeyEntryID(String macAlgorithm,
                                       int keyLengthBits)
         throws CryptoManagerException;
  /**
   * For the specified key entry identifier, instantiate a MAC engine.
   *
   * @param keyEntryID The identifier of the key entry containing the
   * desired MAC algorithm name and key length.
   *
   * @return The MAC engine instantiated with the parameters from the
   * referenced key entry, or null if no such entry exists.
   *
   * @throws CryptoManagerException  In case the key entry identifier
   * is invalid or there is a problem instantiating the MAC engine
   * from the parameters in the referenced key entry.
   */
  Mac getMacEngine(String keyEntryID)
          throws CryptoManagerException;
  /**
   * Encrypts the data in the provided byte array using the preferred
   * cipher transformation.
   *
   * @param  data  The plain-text data to be encrypted.
   *
   * @return  A byte array containing the encrypted representation of
   *          the provided data.
   *
   * @throws java.security.GeneralSecurityException  If a problem
   * occurs while encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  byte[] encrypt(byte[] data)
         throws GeneralSecurityException, CryptoManagerException;
  /**
   * Encrypts the data in the provided byte array using the requested
   * cipher algorithm.
   *
   * @param  cipherTransformation  The algorithm/mode/padding to use
   *         for the cipher.
   *
   * @param  keyLengthBits  The length in bits of the encryption key
   *         this method is to use. Note the specified key length and
   *         transformation must be compatible.
   *
   * @param  data  The plain-text data to be encrypted.
   *
   * @return  A byte array containing the encrypted representation of
   *          the provided data.
   *
   * @throws  java.security.GeneralSecurityException  If a problem
   * occurs while encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  byte[] encrypt(String cipherTransformation,
                        int keyLengthBits,
                        byte[] data)
         throws GeneralSecurityException, CryptoManagerException;
  /**
   * Writes encrypted data to the provided output stream using the
   * preferred cipher transformation.
   *
   * @param  outputStream The output stream to be wrapped by the
   *         returned cipher output stream.
   *
   * @return  The output stream wrapped with a CipherOutputStream.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  CipherOutputStream getCipherOutputStream(
          OutputStream outputStream) throws CryptoManagerException;
  /**
   * Writes encrypted data to the provided output stream using the
   * requested cipher transformation.
   *
   * @param  cipherTransformation  The algorithm/mode/padding to use
   *         for the cipher.
   *
   * @param  keyLengthBits  The length in bits of the encryption key
   *         this method will generate. Note the specified key length
   *         must be compatible with the transformation.
   *
   * @param  outputStream The output stream to be wrapped by the
   *         returned cipher output stream.
   *
   * @return  The output stream wrapped with a CipherOutputStream.
   *
   * @throws  CryptoManagerException  If a problem occurs managing the
   *          encryption key or producing the cipher.
   */
  CipherOutputStream getCipherOutputStream(
          String cipherTransformation, int keyLengthBits,
          OutputStream outputStream)
         throws CryptoManagerException;
  /**
   * Decrypts the data in the provided byte array using cipher
   * specified by the key identifier prologue to the data.
   * cipher.
   *
   * @param  data  The cipher-text data to be decrypted.
   *
   * @return  A byte array containing the clear-text representation of
   *          the provided data.
   *
   * @throws  java.security.GeneralSecurityException  If a problem
   * occurs while encrypting the data.
   *
   * @throws  CryptoManagerException  If a problem occurs reading the
   *          key identifier or initialization vector from the data
   *          prologue, or using these values to initialize a Cipher.
   */
  byte[] decrypt(byte[] data)
         throws GeneralSecurityException,
                CryptoManagerException;
  /**
   * Returns a CipherInputStream instantiated with a cipher
   * corresponding to the key identifier prologue to the data.
   *
   * @param  inputStream The input stream be wrapped with the
   *         CipherInputStream.
   *
   * @return The CiperInputStream instantiated as specified.
   *
   * @throws  CryptoManagerException If there is a problem reading the
   *          key ID or initialization vector from the input stream,
   *          or using these values to inititalize a Cipher.
   */
  CipherInputStream getCipherInputStream(
          InputStream inputStream) throws CryptoManagerException;
  /**
   * Attempts to compress the data in the provided source array into
   * the given destination array.  If the compressed data will fit
   * into the destination array, then this method will return the
   * number of bytes of compressed data in the array.  Otherwise, it
   * will return -1 to indicate that the compression was not
   * successful.  Note that if -1 is returned, then the data in the
   * destination array should be considered invalid.
   *
   * @param  src  The array containing the raw data to compress.
   * @param  dst  The array into which the compressed data should be
   *              written.
   *
   * @return  The number of bytes of compressed data, or -1 if it was
   *          not possible to actually compress the data.
   */
  int compress(byte[] src, byte[] dst);
  /**
   * Attempts to uncompress the data in the provided source array into
   * the given destination array.  If the uncompressed data will fit
   * into the given destination array, then this method will return
   * the number of bytes of uncompressed data written into the
   * destination buffer.  Otherwise, it will return a negative value
   * to indicate that the destination buffer was not large enough.
   * The absolute value of that negative return value will indicate
   * the buffer size required to fully decompress the data.  Note that
   * if a negative value is returned, then the data in the destination
   * array should be considered invalid.
   *
   * @param  src  The array containing the compressed data.
   * @param  dst  The array into which the uncompressed data should be
   *              written.
   *
   * @return  A positive value containing the number of bytes of
   *          uncompressed data written into the destination buffer,
   *          or a negative value whose absolute value is the size of
   *          the destination buffer required to fully decompress the
   *          provided data.
   *
   * @throws java.util.zip.DataFormatException  If a problem occurs
   * while attempting to uncompress the data.
   */
  int uncompress(byte[] src, byte[] dst)
         throws DataFormatException;
  /**
   * Create an SSL context that may be used for communication to
   * another ADS component.
   *
   * @param sslCertNickname The name of the local certificate to use,
   *                        or null if none is specified.
   * @return A new SSL Context.
   * @throws org.opends.server.config.ConfigException If the context
   * could not be created.
   */
  SSLContext getSslContext(String sslCertNickname)
       throws ConfigException;
  /**
   * Get the name of the local certificate to use for SSL.
   * @return The name of the local certificate to use for SSL.
   */
  String getSslCertNickname();
  /**
   * Determine whether SSL encryption is enabled.
   * @return true if SSL encryption is enabled.
   */
  boolean isSslEncryption();
  /**
   * Get the set of enabled SSL protocols.
   * @return The set of enabled SSL protocols.
   */
  SortedSet<String> getSslProtocols();
  /**
   * Get the set of enabled SSL cipher suites.
   * @return The set of enabled SSL cipher suites.
   */
  SortedSet<String> getSslCipherSuites();
}
opends/src/server/org/opends/server/types/CryptoManagerException.java
@@ -34,6 +34,11 @@
 * problems with encryption key managagment, and is a wrapper for a
 * variety of other cipher related exceptions.
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=false,
     mayExtend=false,
     mayInvoke=true)
public class CryptoManagerException extends OpenDsException {
  /**
   * The serial version identifier required to satisfy the compiler
opends/src/server/org/opends/server/types/DirectoryConfig.java
@@ -48,7 +48,6 @@
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.crypto.CryptoManager;
/**
opends/tests/unit-tests-testng/src/server/org/opends/server/crypto/CryptoManagerTestCase.java
@@ -94,9 +94,9 @@
  @Test
  public void testGetInstanceKeyCertificate()
          throws Exception {
    final CryptoManager cm = DirectoryServer.getCryptoManager();
    final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
    final byte[] cert
            = CryptoManager.getInstanceKeyCertificateFromLocalTruststore();
            = CryptoManagerImpl.getInstanceKeyCertificateFromLocalTruststore();
    assertNotNull(cert);
    // The certificate should now be accessible in the truststore backend via LDAP.
@@ -129,8 +129,8 @@
         md.digest(ldapCert)).equals(cm.getInstanceKeyID()));
    // Call twice to ensure idempotent. 
    CryptoManager.publishInstanceKeyEntryInADS();
    CryptoManager.publishInstanceKeyEntryInADS();
    CryptoManagerImpl.publishInstanceKeyEntryInADS();
    CryptoManagerImpl.publishInstanceKeyEntryInADS();
  }
  @Test
@@ -254,7 +254,7 @@
  @Test(dataProvider="cipherParametersData")
  public void testStreamEncryptDecryptSuccess(CipherParameters cp)
          throws Exception {
    final CryptoManager cm = DirectoryServer.getCryptoManager();
    final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
    final String secretMessage = "56789";
    final File tempFile
opends/tests/unit-tests-testng/src/server/org/opends/server/crypto/GetSymmetricKeyExtendedOperationTestCase.java
@@ -30,7 +30,6 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.extensions.ExtensionsTestCase;
import org.opends.server.schema.DirectoryStringSyntax;
import org.opends.server.config.ConfigConstants;
import org.opends.server.types.*;
@@ -68,12 +67,12 @@
  @Test(enabled=true)
  public void testValidRequest() throws Exception
  {
    final CryptoManager cm = DirectoryServer.getCryptoManager();
    final CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
    final String secretMessage = "zyxwvutsrqponmlkjihgfedcba";
    final String cipherTransformationName = "AES/CBC/PKCS5Padding";
    final int cipherKeyLength = 128;
    CryptoManager.publishInstanceKeyEntryInADS();
    CryptoManagerImpl.publishInstanceKeyEntryInADS();
    // Initial encryption ensures a cipher key entry is in ADS.
    cm.encrypt(cipherTransformationName, cipherKeyLength,
@@ -154,7 +153,7 @@
  @Test()
  public void testInvalidRequest() throws Exception
  {
    CryptoManager cm = DirectoryServer.getCryptoManager();
    CryptoManagerImpl cm = DirectoryServer.getCryptoManager();
    String symmetricKey = "1";
    String instanceKeyID = cm.getInstanceKeyID();