From e37ed313cbf50f607d43de83dac3e0f87f2a9381 Mon Sep 17 00:00:00 2001
From: david_page <david_page@localhost>
Date: Sat, 08 Sep 2007 03:46:06 +0000
Subject: [PATCH] Issue 466 partial

---
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java |   52 ++++
 opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java                                 |  617 +++++++++++++++++++++++++++------------------------
 2 files changed, 377 insertions(+), 292 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
index 1e8f79a..8c23199 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/CryptoManager.java
@@ -87,11 +87,11 @@
 
   // The secure random number generator used for key generation,
   // initialization vector PRNG seed...
-  private final SecureRandom secureRandom = new SecureRandom();
+  private static final SecureRandom secureRandom = new SecureRandom();
 
   // The random number generator used for initialization vector
   // production.
-  private final Random pseudoRandom
+  private static final Random pseudoRandom
           = new Random(secureRandom.nextLong());
 
   // The map from encryption key ID to KeyEntry (cache).
@@ -644,294 +644,358 @@
   }
 
 
-
   /**
    * This class corresponds to the encryption key entry in ADS. It is
-   * used for the local cache of key entries requested by
-   * CryptoManager users.
+   * used in the local cache of key entries that have been requested
+   * by CryptoManager clients.
    */
   private static class KeyEntry
   {
     /**
-     * Construct an instance of KeyEntry using the specified
-     * parameters.
+     * Retrieve a KeyEntry from the KeyEntry Map based on cipher
+     * transformation name and key length. If the parameters are valid
+     * but a corresponding entry does not exist in the supplied map,
+     * create the entry and add it to the map.
      *
-     * @param keyID The unique identifier of this cipher
-     *  transformation/key pair.
-     * @param transformation The cipher transformation name.
-     * @param encryptionKey The cipher key.
-     * @param initializationVectorLength The length of the required
-     * initialization vector or 0 if none is required.
+     * @param map The KeyEntry Map containing the key.
+     *
+     * @param transformation The cipher transformation.
+     *
+     * @param keyLengthBits The cipher key length in bits.
+     *
+     * @return The key entry corresponding to the parameters.
+     *
+     * @throws CryptoManagerException If there is a problem
+     * instantiating a Cipher object in order to validate the supplied
+     * parameters when creating a new entry.
      */
-    public KeyEntry( byte[] keyID,
-                     String transformation,
-                     byte[] encryptionKey,
-                     int initializationVectorLength){
-      this.keyID = new byte[keyID.length];
-      System.arraycopy(keyID, 0, this.keyID, 0, keyID.length);
-      this.transformation = new String(transformation);
-      this.encryptionKey = new byte[encryptionKey.length];
-      System.arraycopy(encryptionKey, 0,
-                       this.encryptionKey, 0, encryptionKey.length);
-      this.initializationVectorLength = initializationVectorLength;
+    public static KeyEntry getKeyEntry(
+            final Map<ByteArray,KeyEntry> map,
+            final String transformation,
+            final int keyLengthBits)
+            throws CryptoManagerException {
+      KeyEntry keyEntry = null;
+
+      // search for existing key satisfying request
+      for (Map.Entry<ByteArray, KeyEntry> i: map.entrySet()) {
+        KeyEntry entry = i.getValue();
+        if (! entry.fIsCompromised
+                && entry.fTransformation.equals(transformation)
+                && entry.fKey.length * Byte.SIZE == keyLengthBits) {
+          assert Arrays.equals(i.getKey().array(), entry.fKeyID);
+          keyEntry = entry;
+          break;
+        }
+      }
+
+      if (null == keyEntry) {
+        if (0 != keyLengthBits % Byte.SIZE) {
+          throw new CryptoManagerException(
+                  // TODO: i18n
+                  Message.raw("keyLength parameter must be evenly " +
+                          "divisible by %d.", Byte.SIZE));
+        }
+
+        // TODO: Does ADS monitoring thread keep map updated with keys
+        // produced at other sites? Otherwise, search ADS for suitable
+        // key.
+
+        // generate a new key
+        final byte[] keyID = uuidToBytes(UUID.randomUUID());
+        final byte[] key = new byte[keyLengthBits / Byte.SIZE];
+        secureRandom.nextBytes(key);
+        keyEntry = new KeyEntry(keyID, transformation, key,
+                                /* compute IV length */ -1);
+
+        map.put(new ByteArray(keyID), keyEntry);
+
+        // TODO: publish key to ADS. (mark key "blocked" in map until
+        // registered?)
+      }
+
+      return keyEntry;
     }
 
+
+    /**
+     * Given a key identifier, return the associated key entry.
+     *
+     * @param map  The local cache of key entries.
+     *
+     * @param keyID  The key identifier.
+     *
+     * @return  The key entry associated with the key identifier.
+     */
+    public static KeyEntry getKeyEntry(Map<ByteArray,KeyEntry> map,
+                                       byte[] keyID) {
+      return map.get(new ByteArray(keyID));
+      /* TODO: Does ADS monitorying thread keep map updated with keys
+         produced at other sites? If not, fetch from ADS and update
+         map (assuming a legitimate key ID, the key should exist in
+         ADS because this routine is called for decryption). */
+    }
+
+
+    /**
+     * Construct an instance of KeyEntry using the specified
+     * parameters. This constructor may be used for both locally
+     * generated (new) keys and keys imported from ADS. The parameters
+     * are validated by using them to create and initialize a Cipher
+     * object.
+     *
+     * @param keyID  The unique identifier of this cipher
+     *  transformation/key pair.
+     *
+     * @param transformation  The cipher transformation name.
+     *
+     * @param key  The cipher key.
+     *
+     * @param ivLengthBits  The length in bits of a mandatory
+     * initialization vector or 0 if none is required. Set this
+     * parameter to -1 when generating a new encryption key and this
+     * method will attempt to compute the proper value by first using
+     * the cipher block size and then, if the cipher block size is
+     * non-zero, using 0 (i.e., no initialization vector).
+     *
+     * @throws  CryptoManagerException If there is a problem
+     * instantiating a Cipher object in order to validate the supplied
+     * parameters when creating a new entry.
+     */
+    public KeyEntry( final byte[] keyID, final String transformation,
+                     final byte[] key, final int ivLengthBits)
+            throws CryptoManagerException {
+      // copy arguments
+      this.fKeyID = new byte[keyID.length];
+      System.arraycopy(keyID, 0, this.fKeyID, 0, keyID.length);
+      this.fTransformation = new String(transformation);
+      this.fKey = new byte[key.length];
+      System.arraycopy(key, 0,
+                       this.fKey, 0, key.length);
+      this.fIVLengthBits = ivLengthBits;
+
+      // validate the entry.
+      getCipher(Cipher.ENCRYPT_MODE, null);
+    }
+
+
     /**
      * The unique identifier of this cipher transformation/key pair.
+     *
+     * @return The unique identifier of this cipher transformation/key
+     * pair.
      */
-    public final byte[] keyID;
+    public byte[] getKeyID() {
+      return fKeyID;
+    }
+
 
     /**
      * The cipher transformation name.
+     *
+     * @return The cipher transformation name.
      */
-    public final String transformation;
+    public String getTransformation() {
+      return fTransformation;
+    }
+
 
     /**
      * The cipher key.
+     *
+     * @return The cipher key.
      */
-    public final byte[] encryptionKey;
+    public byte[] getKey() {
+      return fKey;
+    }
+
 
     /**
-     * The initializationVectorLength (may be 0).
+     * The initialization vector length in bits: 0 is a stream cipher
+     * or a block cipher that does not use an IV (e.g., ECB); or a
+     * positive integer, typically the block size of the cipher.
+     *
+     * @return The initialization vector length.
      */
-    public final int initializationVectorLength;
-  }
-
-
-
-  /**
-   * Retrieves an encryption mode cipher using the specified
-   * algorithm.
-   *
-   * @param  cipherTransformation  The algorithm/mode/padding to use
-   *         for the cipher.
-   *
-   * @param  keyLength  The length in bits of the encryption key this
-   *         method will generate. Note the specified key length must
-   *         be compatible with the transformation.
-   *
-   * @param  keyIdentifier  The key identifier this method assigns to
-   *         the cipher transformation/key/initializationVectorLength.
-   *
-   * @return  An encryption mode cipher using the preferred algorithm.
-   *
-   * @throws  NoSuchAlgorithmException  If the requested algorithm is
-   *                                    not supported or is
-   *                                    unavailable.
-   *
-   * @throws  NoSuchPaddingException  If the requested padding
-   *                                  mechanism is not supported or is
-   *                                  unavailable.
-   *
-   * @throws  InvalidKeyException  If the provided key is not
-   *                               appropriate for use with the
-   *                               requested cipher algorithm.
-   *
-   * @throws  InvalidAlgorithmParameterException
-   *               If an internal problem occurs as a result of the
-   *               initialization vector used.
-   *
-   * @throws  CryptoManagerException If there is a problem managing
-   *          or generating an encryption key.
-   */
-  private Cipher getEncryptionCipher(String cipherTransformation,
-                                     int keyLength,
-                                     byte[] keyIdentifier)
-         throws NoSuchAlgorithmException, NoSuchPaddingException,
-                InvalidKeyException,
-                InvalidAlgorithmParameterException,
-                CryptoManagerException
-  {
-    Validator.ensureNotNull(cipherTransformation, keyIdentifier);
-
-    KeyEntry keyEntry = null;
-    for (Map.Entry<ByteArray, KeyEntry> keyEntryRow
-            : keyEntryMap.entrySet()) {
-      KeyEntry keyEntryIterator = keyEntryRow.getValue();
-      if (keyEntryIterator.transformation.equals(cipherTransformation)
-              && keyEntryIterator.encryptionKey.length == keyLength) {
-        assert Arrays.equals(keyEntryRow.getKey().array(),
-                keyEntryIterator.keyID);
-        keyEntry = keyEntryIterator;
-        break;
-      }
+    public int getIVLength() {
+      assert 0 <= fIVLengthBits : "The field was not initialized.";
+      return fIVLengthBits;
     }
 
-    final Cipher cipher = Cipher.getInstance(cipherTransformation);
-    if (null == keyEntry) {
-      // generate a new key
-      if (0 != keyLength % Byte.SIZE) {
+
+    /**
+     * This method produces and initialized Cipher based on this
+     * KeyEntry's state and the method parameters.
+     *
+     * @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.
+     */
+    public Cipher getCipher(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 != fIVLengthBits
+              || Cipher.ENCRYPT_MODE == mode);
+      Validator.ensureTrue(null == initializationVector
+              || initializationVector.length * Byte.SIZE
+                                                    == fIVLengthBits);
+
+      Cipher cipher;
+      try {
+        cipher = Cipher.getInstance(fTransformation);
+      }
+      catch (GeneralSecurityException ex) {
+        // NoSuchAlgorithmException, NoSuchPaddingException
         throw new CryptoManagerException(
                 // TODO: i18n
-                Message.raw("keyLength parameter must be evenly " +
-                        "divisible by %d.", Byte.SIZE));
+                Message.raw("Invalid cipher transformation %s.",
+                        fTransformation), ex);
       }
-      final byte[] keyID = uuidToBytes(UUID.randomUUID());
-      byte[] secretKey = new byte[keyLength/Byte.SIZE];
-      secureRandom.nextBytes(secretKey);
-      final int ivLength = (0 == cipher.getBlockSize())
-                           ? 0 : keyLength;
-      keyEntry = new KeyEntry(keyID, cipherTransformation,
-                              secretKey, ivLength);
+
+      try {
+        if (-1 == fIVLengthBits) {
+          /* Unknown initialization vector length on encryption. This
+             method will first try the cipher block size, then, if
+             that is non-zero and rejected, retry without an
+             initialization vector. */
+          fIVLengthBits = cipher.getBlockSize() * Byte.SIZE;
+        }
+
+        // E.g., AES/CBC/PKCS5Padding -> AES
+        final int separatorIndex = fTransformation.indexOf('/');
+        final String cipherAlgorithm = (0 < separatorIndex)
+                ? fTransformation.substring(0, separatorIndex)
+                : fTransformation;
+
+        if (0 < fIVLengthBits) {
+          try {
+            byte[] iv;
+            if (Cipher.ENCRYPT_MODE == mode
+                    && null == initializationVector) {
+              iv = new byte[fIVLengthBits / Byte.SIZE];
+              pseudoRandom.nextBytes(iv);
+            }
+            else {
+              iv = initializationVector;
+            }
+            cipher.init(mode,
+                   new SecretKeySpec(fKey, cipherAlgorithm),
+                   new IvParameterSpec(iv));
+          }
+          catch (InvalidAlgorithmParameterException ex) {
+            if (Cipher.ENCRYPT_MODE == mode) {
+              /* Some block cipher modes (e.g., ECB) and all stream
+                 ciphers do not use an initialization vector. Set
+                 length to 0 and retry below */
+              fIVLengthBits = 0;
+            }
+            else {
+              throw ex;
+            }
+          }
+        }
+
+        if (0 == fIVLengthBits) {
+          cipher.init(mode, new SecretKeySpec(fKey, cipherAlgorithm));
+        }
+      }
+      catch (GeneralSecurityException ex) {
+        // InvalidKeyException, InvalidAlgorithmParameterException
+        throw new CryptoManagerException(
+                // TODO: i18n
+                Message.raw("Error initializing cipher."), ex);
+      }
+
+      return cipher;
     }
 
-    // E.g., AES/CBC/PKCS5Padding -> AES
-    final int separatorIndex = cipherTransformation.indexOf('/');
-    final String cipherAlgorithm = (0 < separatorIndex)
-            ? cipherTransformation.substring(0, separatorIndex)
-            : cipherTransformation;
 
-    if (0 < keyEntry.initializationVectorLength) {
-      final byte[] initializationVector
-            = new byte[keyEntry.initializationVectorLength/Byte.SIZE];
-      pseudoRandom.nextBytes(initializationVector);
-      cipher.init(Cipher.ENCRYPT_MODE,
-           new SecretKeySpec(keyEntry.encryptionKey, cipherAlgorithm),
-           new IvParameterSpec(initializationVector));
-    }
-    else {
-      cipher.init(Cipher.ENCRYPT_MODE,
-          new SecretKeySpec(keyEntry.encryptionKey, cipherAlgorithm));
+    /**
+     * Mark a key entry as compromised. The entry will no longer be
+     * eligible for use as an encryption key.
+     */
+    public void setIsCompromised() {
+      // TODO: called from ADS monitoring thread. Lock entry?
+      fIsCompromised = true;
     }
 
-    try {
-      System.arraycopy(keyEntry.keyID, 0, keyIdentifier, 0,
-                                               keyIdentifier.length);
-    }
-    catch (Exception ex) {
-      throw new CryptoManagerException(
-                    // TODO: i18n
-                    Message.raw("Error copying out key identifier."),
-                    ex);
-    }
-
-    keyEntryMap.put(new ByteArray(keyEntry.keyID), keyEntry);
-
-    return cipher;
+    // state
+    private final byte[] fKeyID;
+    private final String fTransformation;
+    private final byte[] fKey;
+    private int fIVLengthBits;
+    private boolean fIsCompromised = false;
   }
 
 
-
-  /**
-   * Retrieves a decryption mode cipher using the algorithm and
-   * encryption key specified by the key identifier and the supplied
-   * initialization vector.
-   *
-   * @param  keyEntry The keyEntry containing the key to be used for
-   *         decryption.
-   *
-   * @param  initializationVector The initialization vector to be used
-   *         for decryption.
-   *
-   * @return  A decryption mode cipher using the specified parameters.
-   *
-   * @throws  NoSuchAlgorithmException  If the requested algorithm is
-   *                                    not supported or is
-   *                                    unavailable.
-   *
-   * @throws  NoSuchPaddingException  If the requested padding
-   *                                  mechanism is not supported or is
-   *                                  unavailable.
-   *
-   * @throws  InvalidKeyException  If the provided key is not
-   *                               appropriate for use with the
-   *                               requested cipher algorithm.
-   *
-   * @throws  InvalidAlgorithmParameterException
-   *               If an internal problem occurs as a result of the
-   *               initialization vector used.
-   *
-   * @throws  CryptoManagerException If an invalid keyID is supplied.
-   */
-  private Cipher getDecryptionCipher(KeyEntry keyEntry,
-                                     byte[] initializationVector)
-         throws NoSuchAlgorithmException, NoSuchPaddingException,
-                InvalidKeyException,
-                InvalidAlgorithmParameterException,
-                CryptoManagerException
-  {
-    Validator.ensureNotNull(keyEntry);
-    Validator.ensureTrue(0 == keyEntry.initializationVectorLength
-                            || null != initializationVector);
-    final Cipher cipher = Cipher.getInstance(keyEntry.transformation);
-
-    // E.g., AES/CBC/PKCS5Padding -> AES
-    final int separatorIndex = keyEntry.transformation.indexOf('/');
-    final String cipherAlgorithm = (0 < separatorIndex)
-            ? keyEntry.transformation.substring(0, separatorIndex)
-            : keyEntry.transformation;
-
-    if (0 < keyEntry.initializationVectorLength) {
-      cipher.init(Cipher.DECRYPT_MODE,
-           new SecretKeySpec(keyEntry.encryptionKey, cipherAlgorithm),
-           new IvParameterSpec(initializationVector));
-    }
-    else {
-      cipher.init(Cipher.DECRYPT_MODE,
-          new SecretKeySpec(keyEntry.encryptionKey, cipherAlgorithm));
-    }
-
-    return cipher;
-  }
-
-
-
   /**
    * Encrypts the data in the provided byte array using the preferred
    * cipher transformation.
    *
-   * @param  data  The data to be encrypted.
+   * @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
-   *                                    attempting to encrypt the
-   *                                    data.
+   *          encrypting the data.
    *
    * @throws  CryptoManagerException  If a problem occurs managing the
-   *          encryption key.
+   *          encryption key or producing the cipher.
    */
   public byte[] encrypt(byte[] data)
-         throws GeneralSecurityException,
-                CryptoManagerException
+         throws GeneralSecurityException, CryptoManagerException
   {
     return encrypt(preferredCipherTransformation,
                    preferredCipherTransformationKeyLength, data);
   }
 
 
-
   /**
    * Encrypts the data in the provided byte array using the requested
    * cipher algorithm.
    *
-   * @param  cipherTransformation  The algorithm to use to encrypt the
-   *                          data.
+   * @param  cipherTransformation  The algorithm/mode/padding to use
+   *         for the cipher.
    *
-   * @param  keyLength  The length of the encrytion key to use.
+   * @param  keyLength  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 data to be encrypted.
+   * @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
-   *                                    attempting to encrypt the
-   *                                    data.
+   *          encrypting the data.
    *
    * @throws  CryptoManagerException  If a problem occurs managing the
-   *          encryption key.
+   *          encryption key or producing the cipher.
    */
   public byte[] encrypt(String cipherTransformation, int keyLength,
                         byte[] data)
          throws GeneralSecurityException, CryptoManagerException
   {
-    Validator.ensureNotNull(cipherTransformation, data);
+    Validator.ensureNotNull(cipherTransformation, keyLength, data);
 
-    final byte[] keyID = new byte[16];// FIXME: key id length constant
-    final Cipher cipher = getEncryptionCipher(cipherTransformation,
-                                              keyLength, keyID);
+    final KeyEntry keyEntry = KeyEntry.getKeyEntry(keyEntryMap,
+          cipherTransformation, keyLength);
+    final Cipher cipher
+            = keyEntry.getCipher(Cipher.ENCRYPT_MODE, null);
+    final byte[] keyID = keyEntry.getKeyID();
     final byte[] iv = cipher.getIV();
     final int prologueLength
             = keyID.length + ((null == iv) ? 0 : iv.length);
@@ -947,64 +1011,58 @@
   }
 
 
-
   /**
-   * Encrypts the data in the provided byte array using the preferred
-   * cipher algorithm.
+   * Writes encrypted data to the provided output stream using the
+   * preferred cipher transformation.
    *
    * @param  outputStream The output stream to be wrapped by the
-   *         returned output stream.
+   *         returned cipher output stream.
    *
-   * @return  A byte array containing the encrypted representation of
-   *          the provided data.
-   *
-   * @throws  GeneralSecurityException  If a problem occurs while
-   *                                    attempting to encrypt the
-   *                                    data.
+   * @return  The output stream wrapped with a CipherOutputStream.
    *
    * @throws  CryptoManagerException  If a problem occurs managing the
-   *          encryption key.
+   *          encryption key or producing the cipher.
    */
   public CipherOutputStream getCipherOutputStream(
-          OutputStream outputStream)
-          throws GeneralSecurityException, CryptoManagerException
+          OutputStream outputStream) throws CryptoManagerException
   {
     return getCipherOutputStream(preferredCipherTransformation,
-            preferredCipherTransformationKeyLength, outputStream);
+                preferredCipherTransformationKeyLength, outputStream);
   }
 
 
-
   /**
-   * Encrypts the data in the provided output stream using the
+   * Writes encrypted data to the provided output stream using the
    * requested cipher transformation.
    *
-   * @param  cipherTransformation  The algorithm to use to encrypt the
-   *                          data.
+   * @param  cipherTransformation  The algorithm/mode/padding to use
+   *         for the cipher.
    *
-   * @param  keyLength  The length of the encrytion key to use.
+   * @param  keyLength  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 output stream.
+   *         returned cipher output stream.
    *
-   * @return  A byte array containing the encrypted representation of
-   *          the provided data.
-   *
-   * @throws  GeneralSecurityException  If a problem occurs while
-   *                                    attempting to encrypt the
-   *                                    data.
+   * @return  The output stream wrapped with a CipherOutputStream.
    *
    * @throws  CryptoManagerException  If a problem occurs managing the
-   *          encryption key.
+   *          encryption key or producing the cipher.
    */
   public CipherOutputStream getCipherOutputStream(
           String cipherTransformation, int keyLength,
           OutputStream outputStream)
-         throws GeneralSecurityException, CryptoManagerException
+         throws CryptoManagerException
   {
-    final byte[] keyID = new byte[16];// FIXME: key id length constant
-    final Cipher cipher = getEncryptionCipher(cipherTransformation,
-                                              keyLength, keyID);
+    Validator.ensureNotNull(cipherTransformation, keyLength,
+            outputStream);
+
+    final KeyEntry keyEntry = KeyEntry.getKeyEntry(keyEntryMap,
+          cipherTransformation, keyLength);
+    final Cipher cipher
+            = keyEntry.getCipher(Cipher.ENCRYPT_MODE, null);
+    final byte[] keyID = keyEntry.getKeyID();
     try {
       outputStream.write(keyID);
       if (null != cipher.getIV()) {
@@ -1013,32 +1071,31 @@
     }
     catch (IOException ioe) {
       throw new CryptoManagerException(
-        // TODO: i18n
-        Message.raw("Exception when writing CryptoManager prologue."),
-        ioe);
+              // TODO: i18n
+              Message.raw("Exception when writing CryptoManager" +
+                      " prologue."), ioe);
     }
+
     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 data to be decrypted.
+   * @param  data  The cipher-text data to be decrypted.
    *
-   * @return  A byte array containing the cleartext representation of
+   * @return  A byte array containing the clear-text representation of
    *          the provided data.
    *
    * @throws  GeneralSecurityException  If a problem occurs while
-   *                                    attempting to decrypt the
-   *                                    data.
+   *          encrypting the data.
    *
    * @throws  CryptoManagerException  If a problem occurs reading the
    *          key identifier or initialization vector from the data
-   *          prologue.
+   *          prologue, or using these values to initialize a Cipher.
    */
   public byte[] decrypt(byte[] data)
          throws GeneralSecurityException,
@@ -1052,19 +1109,21 @@
       throw new CryptoManagerException(
               // TODO: i18n
               Message.raw(
-         "Exception when reading key identifier from data prologue."),
-              ex);
+                      "Exception when reading key identifier from" +
+                              " data prologue."), ex);
     }
-    KeyEntry keyEntry = keyEntryMap.get(new ByteArray(keyID));
+
+    KeyEntry keyEntry = KeyEntry.getKeyEntry(keyEntryMap, keyID);
     if (null == keyEntry) {
       throw new CryptoManagerException(
               // TODO: i18N
-             Message.raw("Invalid key identifier in data prologue."));
+              Message.raw("Invalid key identifier in data" +
+                      " prologue."));
     }
 
     byte[] iv = null;
-    if (0 < keyEntry.initializationVectorLength) {
-      iv = new byte[keyEntry.initializationVectorLength/Byte.SIZE];
+    if (0 < keyEntry.getIVLength()) {
+      iv = new byte[keyEntry.getIVLength()/Byte.SIZE];
       try {
         System.arraycopy(data, keyID.length, iv, 0, iv.length);
       }
@@ -1076,7 +1135,7 @@
       }
     }
 
-    final Cipher cipher = getDecryptionCipher(keyEntry, iv);
+    final Cipher cipher = keyEntry.getCipher(Cipher.DECRYPT_MODE, iv);
     final int prologueLength
             = keyID.length + ((null == iv) ? 0 : iv.length);
     return cipher.doFinal(data, prologueLength,
@@ -1094,31 +1153,12 @@
    *
    * @return The CiperInputStream instantiated as specified.
    *
-   * @throws  NoSuchAlgorithmException  If the requested algorithm is
-   *                                    not supported or is
-   *                                    unavailable.
-   *
-   * @throws  NoSuchPaddingException  If the requested padding
-   *                                  mechanism is not supported or is
-   *                                  unavailable.
-   *
-   * @throws  InvalidKeyException  If the provided key is not
-   *                               appropriate for use with the
-   *                               requested cipher algorithm.
-   *
-   * @throws  InvalidAlgorithmParameterException
-   *               If an internal problem occurs as a result of the
-   *               initialization vector used.
-   *
    * @throws  CryptoManagerException If there is a problem reading the
-   *          key ID or initialization vector from the input stream.
+   *          key ID or initialization vector from the input stream,
+   *          or using these values to inititalize a Cipher.
    */
   public CipherInputStream getCipherInputStream(
-                                            InputStream inputStream)
-          throws NoSuchAlgorithmException, NoSuchPaddingException,
-                 InvalidKeyException,
-                 InvalidAlgorithmParameterException,
-                 CryptoManagerException
+          InputStream inputStream) throws CryptoManagerException
   {
     KeyEntry keyEntry;
     byte[] iv = null;
@@ -1131,22 +1171,21 @@
                         " identifier from data prologue."));
       }
 
-      keyEntry = keyEntryMap.get(new ByteArray(keyID));
+      keyEntry = KeyEntry.getKeyEntry(keyEntryMap, keyID);
       if (null == keyEntry) {
         throw new CryptoManagerException(
                 // TODO: i18N
              Message.raw("Invalid key identifier in data prologue."));
       }
 
-      if (0 < keyEntry.initializationVectorLength) {
-        iv = new byte[keyEntry.initializationVectorLength/Byte.SIZE];
+      if (0 < keyEntry.getIVLength()) {
+        iv = new byte[keyEntry.getIVLength() / Byte.SIZE];
         if (iv.length != inputStream.read(iv)) {
           throw new CryptoManagerException(
                   // TODO: i18n
                   Message.raw("Stream underflow when reading" +
                       " initialization vector from data prologue."));
         }
-
       }
     }
     catch (IOException ioe) {
@@ -1158,7 +1197,7 @@
     }
 
     return new CipherInputStream(inputStream,
-                          getDecryptionCipher(keyEntry, iv));
+                         keyEntry.getCipher(Cipher.DECRYPT_MODE, iv));
   }
 
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
index 9c98052..a1480f5 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/CryptoManagerTestCase.java
@@ -45,8 +45,6 @@
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import javax.crypto.Cipher;
-
 /**
  This class tests the CryptoManager.
  */
@@ -129,7 +127,7 @@
     final CryptoManager cm = DirectoryServer.getCryptoManager();
     final String secretMessage = "1234";
 
-    final byte[] cipherText = cm.encrypt("Blowfish/CFB/NoPadding", 64,
+    final byte[] cipherText = cm.encrypt("Blowfish/CFB/NoPadding", 128,
             secretMessage.getBytes());
     assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
 
@@ -262,4 +260,52 @@
     is.close();
     assertEquals(new String(plainText), secretMessage);
   }
+
+  /**
+   Tests a simple encryption-decryption cycle.
+
+   @throws Exception If an exceptional condition arises.
+   */
+  @Test
+  public void testEncryptDecryptSuccessZZ() throws Exception {
+    final CryptoManager cm = DirectoryServer.getCryptoManager();
+    final String secretMessage = "1234";
+
+    final byte[] cipherText = cm.encrypt("DES/ECB/PKCS5Padding", 64,
+            secretMessage.getBytes());
+    assertEquals(-1, (new String(cipherText)).indexOf(secretMessage));
+
+    final byte[] plainText = cm.decrypt(cipherText);
+    assertEquals((new String(plainText)), secretMessage);
+  }
+
+  /**
+   Tests a simple cipher stream encryption-decryption cycle.
+
+   @throws Exception If an exceptional condition arises.
+   */
+  @Test
+  public void testCipherEncryptDecryptSuccessZZ() throws Exception {
+    final CryptoManager cm = DirectoryServer.getCryptoManager();
+    final String secretMessage = "56789";
+
+    final File tempFile
+            = File.createTempFile(cm.getClass().getName(), null);
+    tempFile.deleteOnExit();
+
+    OutputStream os = new FileOutputStream(tempFile);
+    os = cm.getCipherOutputStream("DES/ECB/PKCS5Padding", 64, os);
+    os.write(secretMessage.getBytes());
+    os.close();
+
+    // TODO: check tempfile for plaintext.
+
+    InputStream is = new FileInputStream(tempFile);
+    is = cm.getCipherInputStream(is);
+    byte[] plainText = new byte[secretMessage.getBytes().length];
+    assertEquals(is.read(plainText), secretMessage.getBytes().length);
+    assertEquals(is.read(), -1);
+    is.close();
+    assertEquals(new String(plainText), secretMessage);
+  }
 }

--
Gitblit v1.10.0