From e3deb8b9b6b1e1fbf53309380f5b30da848c507f Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Fri, 12 Sep 2014 14:36:55 +0000
Subject: [PATCH] CR-4097 Big code cleanup of some storage schemes.

---
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA384PasswordStorageSchemeTestCase.java |   59 --
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PBKDF2PasswordStorageSchemeTestCase.java       |    7 
 opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java                                       |  215 +++++-------
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA256PasswordStorageSchemeTestCase.java |   58 --
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageSchemeTestCase.java      |   38 -
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA1PasswordStorageSchemeTestCase.java   |   48 --
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA512PasswordStorageSchemeTestCase.java |   63 --
 opendj3-server-dev/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java                                      |  131 +++---
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java        |  251 --------------
 opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java             |  135 ++++++-
 10 files changed, 333 insertions(+), 672 deletions(-)

diff --git a/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java b/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
index a842995..b3a1908 100644
--- a/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
+++ b/opendj3-server-dev/src/server/org/opends/server/extensions/PBKDF2PasswordStorageScheme.java
@@ -62,14 +62,14 @@
  * implementation uses a configurable number of iterations.
  */
 public class PBKDF2PasswordStorageScheme
-  extends PasswordStorageScheme<PBKDF2PasswordStorageSchemeCfg>
-  implements ConfigurationChangeListener<PBKDF2PasswordStorageSchemeCfg>
+    extends PasswordStorageScheme<PBKDF2PasswordStorageSchemeCfg>
+    implements ConfigurationChangeListener<PBKDF2PasswordStorageSchemeCfg>
 {
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
   /** The fully-qualified name of this class. */
   private static final String CLASS_NAME =
-       "org.opends.server.extensions.PBKDF2PasswordStorageScheme";
+      "org.opends.server.extensions.PBKDF2PasswordStorageScheme";
 
 
   /**
@@ -93,7 +93,6 @@
   /** The current configuration for this storage scheme. */
   private volatile PBKDF2PasswordStorageSchemeCfg config;
 
-
   /**
    * Creates a new instance of this password storage scheme.  Note that no
    * initialization should be performed here, as all initialization should be
@@ -107,8 +106,8 @@
   /** {@inheritDoc} */
   @Override
   public void initializePasswordStorageScheme(
-                   PBKDF2PasswordStorageSchemeCfg configuration)
-         throws ConfigException, InitializationException
+      PBKDF2PasswordStorageSchemeCfg configuration)
+      throws ConfigException, InitializationException
   {
     try
     {
@@ -152,18 +151,13 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodePassword(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = config.getPBKDF2Iterations();
 
-    byte[] digestBytes = getDigestBytes(plaintext, saltBytes, iterations);
-    // Append the salt to the hashed value and base64-the whole thing.
-    byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
-
-    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
-    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length,
-                     NUM_SALT_BYTES);
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes, iterations);
+    byte[] hashPlusSalt = concatenateHashPlusSalt(saltBytes, digestBytes);
 
     return ByteString.valueOf(iterations + ":" + Base64.encode(hashPlusSalt));
   }
@@ -171,7 +165,7 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodePasswordWithScheme(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     return ByteString.valueOf('{' + STORAGE_SCHEME_NAME_PBKDF2 + '}'
         + encodePassword(plaintext));
@@ -186,8 +180,8 @@
     // Base64-decode the remaining value and take the last 8 bytes as the salt.
     try
     {
-      String stored = storedPassword.toString();
-      int pos = stored.indexOf(':');
+      final String stored = storedPassword.toString();
+      final int pos = stored.indexOf(':');
       if (pos == -1)
       {
         throw new Exception();
@@ -217,43 +211,6 @@
     }
   }
 
-  private boolean encodeAndMatch(ByteSequence plaintextPassword,
-      final byte[] saltBytes, byte[] digestBytes, int iterations)
-  {
-    // Use the salt to generate a digest based on the provided plain-text value.
-    int plainBytesLength = plaintextPassword.length();
-    byte[] plainPlusSalt = new byte[plainBytesLength + saltBytes.length];
-    plaintextPassword.copyTo(plainPlusSalt);
-    System.arraycopy(saltBytes, 0, plainPlusSalt, plainBytesLength, saltBytes.length);
-
-
-    char[] plaintextChars = null;
-    synchronized (factoryLock)
-    {
-      try
-      {
-        plaintextChars = plaintextPassword.toString().toCharArray();
-        KeySpec spec = new PBEKeySpec(
-            plaintextChars, saltBytes,
-            iterations, SHA1_LENGTH * 8);
-        final byte[] userDigestBytes = factory.generateSecret(spec).getEncoded();
-        return Arrays.equals(digestBytes, userDigestBytes);
-      }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-        return false;
-      }
-      finally
-      {
-        if (plaintextChars != null)
-        {
-          Arrays.fill(plaintextChars, '0');
-        }
-      }
-    }
-  }
-
   /** {@inheritDoc} */
   @Override
   public boolean supportsAuthPasswordSyntax()
@@ -271,11 +228,11 @@
   /** {@inheritDoc} */
   @Override
   public ByteString encodeAuthPassword(ByteSequence plaintext)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = config.getPBKDF2Iterations();
-    byte[] digestBytes = getDigestBytes(plaintext, saltBytes, iterations);
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes, iterations);
 
     // Encode and return the value.
     return ByteString.valueOf(AUTH_PASSWORD_SCHEME_NAME_PBKDF2 + '$'
@@ -283,41 +240,6 @@
         + Base64.encode(digestBytes));
   }
 
-  private byte[] getDigestBytes(ByteSequence plaintext, byte[] saltBytes,
-      int iterations) throws DirectoryException
-  {
-    char[] plaintextChars = null;
-    synchronized (factoryLock)
-    {
-      try
-      {
-        random.nextBytes(saltBytes);
-
-        plaintextChars = plaintext.toString().toCharArray();
-        KeySpec spec = new PBEKeySpec(
-            plaintextChars, saltBytes,
-            iterations, SHA1_LENGTH * 8);
-        return factory.generateSecret(spec).getEncoded();
-      }
-      catch (Exception e)
-      {
-        logger.traceException(e);
-
-        LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-            CLASS_NAME, getExceptionMessage(e));
-        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-                                     message, e);
-      }
-      finally
-      {
-        if (plaintextChars != null)
-        {
-          Arrays.fill(plaintextChars, '0');
-        }
-      }
-    }
-  }
-
   /** {@inheritDoc} */
   @Override
   public boolean authPasswordMatches(ByteSequence plaintextPassword,
@@ -352,7 +274,7 @@
   /** {@inheritDoc} */
   @Override
   public ByteString getPlaintextValue(ByteSequence storedPassword)
-         throws DirectoryException
+      throws DirectoryException
   {
     LocalizableMessage message =
         ERR_PWSCHEME_NOT_REVERSIBLE.get(STORAGE_SCHEME_NAME_PBKDF2);
@@ -363,7 +285,7 @@
   @Override
   public ByteString getAuthPasswordPlaintextValue(String authInfo,
                                                   String authValue)
-         throws DirectoryException
+      throws DirectoryException
   {
     LocalizableMessage message =
         ERR_PWSCHEME_NOT_REVERSIBLE.get(AUTH_PASSWORD_SCHEME_NAME_PBKDF2);
@@ -386,54 +308,56 @@
    * user password).
    *
    * @param  passwordBytes  The bytes that make up the clear-text password.
-   *
    * @return  The encoded password string, including the scheme name in curly
    *          braces.
-   *
    * @throws  DirectoryException  If a problem occurs during processing.
    */
   public static String encodeOffline(byte[] passwordBytes)
-         throws DirectoryException
+      throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
     int    iterations     = 10000;
 
-    byte[] digestBytes = getDigestBytes(passwordBytes, saltBytes, iterations);
-    // Append the salt to the hashed value and base64-the whole thing.
-    byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
+    final ByteString password = ByteString.wrap(passwordBytes);
+    byte[] digestBytes = encodeWithRandomSalt(password, saltBytes, iterations);
+    byte[] hashPlusSalt = concatenateHashPlusSalt(saltBytes, digestBytes);
 
-    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
-    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length,
-                     NUM_SALT_BYTES);
-
-    return '{' + STORAGE_SCHEME_NAME_PBKDF2 + '}' + iterations + ':' +
-      Base64.encode(hashPlusSalt);
+    return '{' + STORAGE_SCHEME_NAME_PBKDF2 + '}' + iterations + ':'
+        + Base64.encode(hashPlusSalt);
   }
 
-  private static byte[] getDigestBytes(byte[] plaintext, byte[] saltBytes,
+  private static byte[] encodeWithRandomSalt(ByteString plaintext, byte[] saltBytes,
       int iterations) throws DirectoryException
   {
-    char[] plaintextChars = null;
     try
     {
-      SecureRandom.getInstance(SECURE_PRNG_SHA1).nextBytes(saltBytes);
-
-      plaintextChars = plaintext.toString().toCharArray();
-      KeySpec spec = new PBEKeySpec(
-          plaintextChars, saltBytes,
-          iterations, SHA1_LENGTH * 8);
-      return SecretKeyFactory
-          .getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2)
-          .generateSecret(spec).getEncoded();
+      final SecureRandom random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
+      final SecretKeyFactory factory = SecretKeyFactory.getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2);
+      return encodeWithRandomSalt(plaintext, saltBytes, iterations, random, factory);
+    }
+    catch (DirectoryException e)
+    {
+      throw e;
     }
     catch (Exception e)
     {
-      logger.traceException(e);
+      throw cannotEncodePassword(e);
+    }
+  }
 
-      LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-          CLASS_NAME, getExceptionMessage(e));
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message, e);
+  private static byte[] encodeWithSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations, final SecretKeyFactory factory) throws DirectoryException
+  {
+    final char[] plaintextChars = plaintext.toString().toCharArray();
+    try
+    {
+      KeySpec spec =
+          new PBEKeySpec(plaintextChars, saltBytes, iterations, SHA1_LENGTH * 8);
+      return factory.generateSecret(spec).getEncoded();
+    }
+    catch (Exception e)
+    {
+      throw cannotEncodePassword(e);
     }
     finally
     {
@@ -444,4 +368,53 @@
     }
   }
 
+  private boolean encodeAndMatch(ByteSequence plaintext, byte[] saltBytes,
+      byte[] digestBytes, int iterations)
+  {
+    synchronized (factoryLock)
+    {
+      try
+      {
+        final byte[] userDigestBytes =
+            encodeWithSalt(plaintext, saltBytes, iterations, factory);
+        return Arrays.equals(digestBytes, userDigestBytes);
+      }
+      catch (Exception e)
+      {
+        return false;
+      }
+    }
+  }
+
+  private byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations) throws DirectoryException
+  {
+    synchronized (factoryLock)
+    {
+      return encodeWithRandomSalt(plaintext, saltBytes, iterations, random, factory);
+    }
+  }
+
+  private static byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations, SecureRandom random, final SecretKeyFactory factory) throws DirectoryException
+  {
+    random.nextBytes(saltBytes);
+    return encodeWithSalt(plaintext, saltBytes, iterations, factory);
+  }
+
+  private static DirectoryException cannotEncodePassword(Exception e)
+  {
+    logger.traceException(e);
+
+    LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(CLASS_NAME, getExceptionMessage(e));
+    return new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
+  }
+
+  private static byte[] concatenateHashPlusSalt(byte[] saltBytes, byte[] digestBytes) {
+    final byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
+    System.arraycopy(digestBytes, 0, hashPlusSalt, 0, digestBytes.length);
+    System.arraycopy(saltBytes, 0, hashPlusSalt, digestBytes.length, NUM_SALT_BYTES);
+    return hashPlusSalt;
+  }
+
 }
diff --git a/opendj3-server-dev/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java b/opendj3-server-dev/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java
index 014324f..beb1543 100644
--- a/opendj3-server-dev/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java
+++ b/opendj3-server-dev/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageScheme.java
@@ -90,7 +90,6 @@
   /** The secure random number generator to use to generate the salt values. */
   private SecureRandom random;
 
-
   /**
    * Creates a new instance of this password storage scheme.  Note that no
    * initialization should be performed here, as all initialization should be
@@ -131,8 +130,7 @@
       throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
-    byte[] digestBytes = createRandomSaltAndEncode(plaintext, saltBytes);
-    // Append the hashed value to the salt and base64-the whole thing.
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes);
     byte[] hashPlusSalt = concatenateSaltPlusHash(saltBytes, digestBytes);
 
     return ByteString.valueOf(Base64.encode(hashPlusSalt));
@@ -156,7 +154,6 @@
     try
     {
       String stored = storedPassword.toString();
-
       byte[] decodedBytes = Base64.decode(stored);
 
       if (decodedBytes.length != NUM_SALT_BYTES + SHA1_LENGTH)
@@ -167,8 +164,8 @@
       }
 
       final int saltLength = NUM_SALT_BYTES;
-      byte[] saltBytes = new byte[saltLength];
-      byte[] digestBytes = new byte[SHA1_LENGTH];
+      final byte[] digestBytes = new byte[SHA1_LENGTH];
+      final byte[] saltBytes = new byte[saltLength];
       System.arraycopy(decodedBytes, 0, saltBytes, 0, saltLength);
       System.arraycopy(decodedBytes, saltLength, digestBytes, 0, SHA1_LENGTH);
       return encodeAndMatch(plaintextPassword, saltBytes, digestBytes, iterations);
@@ -202,7 +199,7 @@
       throws DirectoryException
   {
     byte[] saltBytes      = new byte[NUM_SALT_BYTES];
-    byte[] digestBytes = createRandomSaltAndEncode(plaintext, saltBytes);
+    byte[] digestBytes = encodeWithRandomSalt(plaintext, saltBytes);
     // Encode and return the value.
     return ByteString.valueOf(AUTH_PASSWORD_SCHEME_NAME_PKCS5S2 + '$'
         + iterations + ':' + Base64.encode(saltBytes) + '$'
@@ -219,7 +216,7 @@
       int pos = authInfo.indexOf(':');
       if (pos == -1)
       {
-        return false;
+        throw new Exception();
       }
       int iterations = Integer.parseInt(authInfo.substring(0, pos));
       byte[] saltBytes   = Base64.decode(authInfo.substring(pos + 1));
@@ -277,87 +274,53 @@
    * user password).
    *
    * @param  passwordBytes  The bytes that make up the clear-text password.
-   *
    * @return  The encoded password string, including the scheme name in curly
    *          braces.
-   *
    * @throws  DirectoryException  If a problem occurs during processing.
    */
   public static String encodeOffline(byte[] passwordBytes)
       throws DirectoryException
   {
     byte[] saltBytes = new byte[NUM_SALT_BYTES];
-    byte[] digestBytes;
-
-    try
-    {
-      SecureRandom.getInstance(SECURE_PRNG_SHA1).nextBytes(saltBytes);
-
-      char[] plaintextChars = Arrays.toString(passwordBytes).toCharArray();
-      KeySpec spec = new PBEKeySpec(plaintextChars, saltBytes,iterations,
-          SHA1_LENGTH * 8);
-      digestBytes = SecretKeyFactory
-          .getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2)
-          .generateSecret(spec).getEncoded();
-    }
-    catch (Exception e)
-    {
-      logger.traceException(e);
-      LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-          CLASS_NAME, getExceptionMessage(e));
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message, e);
-    }
-
-    // Append the hashed value to the salt and base64-the whole thing.
+    byte[] digestBytes = encodeWithRandomSalt(ByteString.wrap(passwordBytes), saltBytes);
     byte[] hashPlusSalt = concatenateSaltPlusHash(saltBytes, digestBytes);
 
     return '{' + STORAGE_SCHEME_NAME_PKCS5S2 + '}' +
         Base64.encode(hashPlusSalt);
   }
 
-
-  private boolean encodeAndMatch(ByteSequence plaintext,
-                                 byte[] saltBytes, byte[] digestBytes, int iterations)
+  private static byte[] encodeWithRandomSalt(ByteString plaintext, byte[] saltBytes)
+      throws DirectoryException
   {
     try
     {
-      byte[] userDigestBytes = encodeWithSalt(plaintext, saltBytes, iterations);
-      return Arrays.equals(digestBytes, userDigestBytes);
+      final SecureRandom random = SecureRandom.getInstance(SECURE_PRNG_SHA1);
+      final SecretKeyFactory factory = SecretKeyFactory.getInstance(MESSAGE_DIGEST_ALGORITHM_PBKDF2);
+      return encodeWithRandomSalt(plaintext, saltBytes, random, factory);
+    }
+    catch (DirectoryException e)
+    {
+      throw e;
     }
     catch (Exception e)
     {
-      return false;
+      throw cannotEncodePassword(e);
     }
   }
 
-
-  private byte[] createRandomSaltAndEncode(ByteSequence plaintext, byte[] saltBytes) throws DirectoryException {
-    synchronized(factoryLock)
-    {
-      random.nextBytes(saltBytes);
-      return encodeWithSalt(plaintext, saltBytes, iterations);
-    }
-  }
-
-  private byte[] encodeWithSalt(ByteSequence plaintext, byte[] saltBytes, int iterations) throws DirectoryException {
-    char[] plaintextChars = null;
+  private static byte[] encodeWithSalt(ByteSequence plaintext, byte[] saltBytes,
+      int iterations, final SecretKeyFactory factory) throws DirectoryException
+  {
+    final char[] plaintextChars = plaintext.toString().toCharArray();
     try
     {
-      plaintextChars = plaintext.toString().toCharArray();
-      KeySpec spec = new PBEKeySpec(
-          plaintextChars, saltBytes,
-          iterations, SHA1_LENGTH * 8);
+      KeySpec spec =
+          new PBEKeySpec(plaintextChars, saltBytes, iterations, SHA1_LENGTH * 8);
       return factory.generateSecret(spec).getEncoded();
     }
     catch (Exception e)
     {
-      logger.traceException(e);
-
-      LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(
-          CLASS_NAME, getExceptionMessage(e));
-      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
-          message, e);
+      throw cannotEncodePassword(e);
     }
     finally
     {
@@ -368,12 +331,52 @@
     }
   }
 
-  private static byte[] concatenateSaltPlusHash(byte[] saltBytes, byte[] digestBytes) {
-    byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
+  private boolean encodeAndMatch(ByteSequence plaintext, byte[] saltBytes,
+      byte[] digestBytes, int iterations)
+  {
+    synchronized (factoryLock)
+    {
+      try
+      {
+        final byte[] userDigestBytes =
+            encodeWithSalt(plaintext, saltBytes, iterations, factory);
+        return Arrays.equals(digestBytes, userDigestBytes);
+      }
+      catch (Exception e)
+      {
+        return false;
+      }
+    }
+  }
 
+  private byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes)
+      throws DirectoryException
+  {
+    synchronized (factoryLock)
+    {
+      return encodeWithRandomSalt(plaintext, saltBytes, random, factory);
+    }
+  }
+
+  private static byte[] encodeWithRandomSalt(ByteSequence plaintext, byte[] saltBytes,
+      SecureRandom random, final SecretKeyFactory factory) throws DirectoryException
+  {
+    random.nextBytes(saltBytes);
+    return encodeWithSalt(plaintext, saltBytes, iterations, factory);
+  }
+
+  private static DirectoryException cannotEncodePassword(Exception e)
+  {
+    logger.traceException(e);
+
+    LocalizableMessage message = ERR_PWSCHEME_CANNOT_ENCODE_PASSWORD.get(CLASS_NAME, getExceptionMessage(e));
+    return new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
+  }
+
+  private static byte[] concatenateSaltPlusHash(byte[] saltBytes, byte[] digestBytes) {
+    final byte[] hashPlusSalt = new byte[digestBytes.length + NUM_SALT_BYTES];
     System.arraycopy(saltBytes, 0, hashPlusSalt, 0, NUM_SALT_BYTES);
-    System.arraycopy(digestBytes, 0, hashPlusSalt, NUM_SALT_BYTES,
-        digestBytes.length);
+    System.arraycopy(digestBytes, 0, hashPlusSalt, NUM_SALT_BYTES, digestBytes.length);
     return hashPlusSalt;
   }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java
index 3e1c234..33606a1 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/CryptPasswordStorageSchemeTestCase.java
@@ -27,43 +27,26 @@
  */
 package org.opends.server.extensions;
 
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-import java.util.ArrayList;
-
+import org.forgerock.opendj.ldap.ByteString;
 import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.server.AdminTestCaseUtils;
 import org.opends.server.admin.std.meta.CryptPasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.CryptPasswordStorageSchemeCfg;
-import org.opends.server.core.DirectoryServer;
-import org.opends.server.core.ModifyOperation;
-import org.opends.server.core.PasswordPolicy;
-import org.opends.server.protocols.internal.InternalClientConnection;
-import org.opends.server.schema.AuthPasswordSyntax;
-import org.opends.server.schema.UserPasswordSyntax;
-import org.opends.server.types.Attributes;
-import org.forgerock.opendj.ldap.ByteString;
-import org.opends.server.types.DN;
-import org.opends.server.types.DirectoryException;
 import org.opends.server.types.Entry;
-import org.opends.server.types.Modification;
-import org.forgerock.opendj.ldap.ModificationType;
-import org.forgerock.opendj.ldap.ResultCode;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.opends.server.extensions.PasswordStorageSchemeTestCase.*;
+
 
 /**
  * A set of test cases for the crypt password storage scheme.
  */
+@SuppressWarnings("javadoc")
 public class CryptPasswordStorageSchemeTestCase
        extends ExtensionsTestCase
 {
-
   // Names of all the crypt algorithms we want to test.
   private static final String[] names = { "unix", "md5", "sha256", "sha512" };
 
@@ -81,7 +64,7 @@
    * Ensures that the Directory Server is started before running any of these
    * tests.
    */
-  @BeforeClass()
+  @BeforeClass
   public void startServer() throws Exception
   {
     TestCaseUtils.startServer();
@@ -99,31 +82,7 @@
   @DataProvider(name = "testPasswords")
   public Object[][] getTestPasswords()
   {
-    return new Object[][]
-    {
-      new Object[] { ByteString.empty() },
-      new Object[] { ByteString.valueOf("") },
-      new Object[] { ByteString.valueOf("\u0000") },
-      new Object[] { ByteString.valueOf("\t") },
-      new Object[] { ByteString.valueOf("\n") },
-      new Object[] { ByteString.valueOf("\r\n") },
-      new Object[] { ByteString.valueOf(" ") },
-      new Object[] { ByteString.valueOf("Test1\tTest2\tTest3") },
-      new Object[] { ByteString.valueOf("Test1\nTest2\nTest3") },
-      new Object[] { ByteString.valueOf("Test1\r\nTest2\r\nTest3") },
-      new Object[] { ByteString.valueOf("a") },
-      new Object[] { ByteString.valueOf("ab") },
-      new Object[] { ByteString.valueOf("abc") },
-      new Object[] { ByteString.valueOf("abcd") },
-      new Object[] { ByteString.valueOf("abcde") },
-      new Object[] { ByteString.valueOf("abcdef") },
-      new Object[] { ByteString.valueOf("abcdefg") },
-      new Object[] { ByteString.valueOf("abcdefgh") },
-      new Object[] { ByteString.valueOf("The Quick Brown Fox Jumps Over " +
-                                         "The Lazy Dog") },
-      new Object[] { ByteString.valueOf("\u00BFD\u00F3nde est\u00E1 el " +
-                                         "ba\u00F1o?") }
-    };
+    return getTestPasswordsStatic();
   }
 
 
@@ -132,7 +91,6 @@
    * provided password, and ensures that the encoded value is correct.
    *
    * @param  plaintext  The plain-text version of the password to encode.
-   *
    * @throws  Exception  If an unexpected problem occurs.
    */
   @Test(dataProvider = "testPasswords")
@@ -141,94 +99,15 @@
   {
     for (String name : names)
     {
-      CryptPasswordStorageScheme scheme = getScheme(name);
-      assertNotNull(scheme);
-      assertNotNull(scheme.getStorageSchemeName());
-
-      ByteString encodedPassword = scheme.encodePassword(plaintext);
-      assertNotNull(encodedPassword);
-      assertTrue(scheme.passwordMatches(plaintext, encodedPassword));
-      assertFalse(scheme.passwordMatches(plaintext,
-                                         ByteString.valueOf("garbage")));
-
-      ByteString schemeEncodedPassword =
-           scheme.encodePasswordWithScheme(plaintext);
-      String[] pwComponents = UserPasswordSyntax.decodeUserPassword(
-                                   schemeEncodedPassword.toString());
-      assertNotNull(pwComponents);
-
-
-      if (scheme.supportsAuthPasswordSyntax())
-      {
-        assertNotNull(scheme.getAuthPasswordSchemeName());
-        ByteString encodedAuthPassword = scheme.encodeAuthPassword(plaintext);
-        StringBuilder[] authPWComponents =
-             AuthPasswordSyntax.decodeAuthPassword(
-                  encodedAuthPassword.toString());
-        assertTrue(scheme.authPasswordMatches(plaintext,
-                                              authPWComponents[1].toString(),
-                                              authPWComponents[2].toString()));
-        assertFalse(scheme.authPasswordMatches(plaintext, ",", "foo"));
-        assertFalse(scheme.authPasswordMatches(plaintext, "foo", ","));
-      }
-      else
-      {
-        try
-        {
-          scheme.encodeAuthPassword(plaintext);
-          throw new Exception("Expected encodedAuthPassword to fail for scheme " +
-                              scheme.getStorageSchemeName() +
-                              " because it doesn't support auth passwords.");
-        }
-        catch (DirectoryException de)
-        {
-          // This was expected.
-        }
-
-        assertFalse(scheme.authPasswordMatches(plaintext, "foo", "bar"));
-      }
-
-
-      if (scheme.isReversible())
-      {
-        assertEquals(scheme.getPlaintextValue(encodedPassword), plaintext);
-      }
-      else
-      {
-        try
-        {
-          scheme.getPlaintextValue(encodedPassword);
-          throw new Exception("Expected getPlaintextValue to fail for scheme " +
-                              scheme.getStorageSchemeName() +
-                              " because it is not reversible.");
-        }
-        catch (DirectoryException de)
-        {
-          // This was expected.
-        }
-      }
-
-      scheme.isStorageSchemeSecure();
-
+      testStorageScheme(plaintext, getScheme(name));
     }
   }
 
 
-
   @DataProvider
   public Object[][] passwordsForBinding()
   {
-    return new Object[][]
-    {
-      // In the case of a clear-text password, these values will be shoved
-      // un-excaped into an LDIF file, so make sure they don't include \n
-      // or other characters that will cause LDIF parsing errors.
-      // We really don't need many test cases here, since that functionality
-      // is tested above.
-      new Object[] { ByteString.valueOf("a") },
-      new Object[] { ByteString.valueOf("abcdefgh") },
-      new Object[] { ByteString.valueOf("abcdefghi") },
-    };
+    return PasswordStorageSchemeTestCase.passwordsForBinding();
   }
 
 
@@ -243,88 +122,10 @@
   {
     for (String name: names)
     {
-      // Start/clear-out the memory backend
-      TestCaseUtils.initializeTestBackend(true);
-
-      setAllowPreencodedPasswords(true);
-
-      CryptPasswordStorageScheme scheme = getScheme(name);
-      ByteString schemeEncodedPassword =
-          scheme.encodePasswordWithScheme(plainPassword);
-
-      //
-      // This code creates a user with the encoded password,
-      // and then verifies that they can bind with the raw password.
-      //
-
-      Entry userEntry = TestCaseUtils.makeEntry(
-          "dn: uid=test.user,o=test",
-          "objectClass: top",
-          "objectClass: person",
-          "objectClass: organizationalPerson",
-          "objectClass: inetOrgPerson",
-          "uid: test.user",
-          "givenName: Test",
-          "sn: User",
-          "cn: Test User",
-          "ds-privilege-name: bypass-acl",
-          "userPassword: " + schemeEncodedPassword.toString());
-
-      // Add the entry
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=test.user,o=test",
-          plainPassword.toString()),
-          "Failed to bind when pre-encoded password = \"" +
-              schemeEncodedPassword.toString() + "\" and " +
-              "plaintext password = \"" +
-              plainPassword.toString() + "\"");
+      testSettingEncodedPassword(plainPassword, getScheme(name));
     }
   }
 
-
-  /**
-   * Sets whether or not to allow pre-encoded password values for the
-   * current password storage scheme and returns the previous value so that
-   * it can be restored.
-   *
-   * @param allowPreencoded whether or not to allow pre-encoded passwords
-   * @return the previous value for the allow preencoded passwords
-   */
-  private boolean setAllowPreencodedPasswords(boolean allowPreencoded)
-          throws Exception
-  {
-    // This code was borrowed from
-    // PasswordPolicyTestCase.testAllowPreEncodedPasswordsAuth
-    boolean previousValue = false;
-    try {
-      DN dn = DN.valueOf("cn=Default Password Policy,cn=Password Policies,cn=config");
-      PasswordPolicy p = (PasswordPolicy) DirectoryServer.getAuthenticationPolicy(dn);
-      previousValue = p.isAllowPreEncodedPasswords();
-
-      String attr  = "ds-cfg-allow-pre-encoded-passwords";
-
-      ArrayList<Modification> mods = new ArrayList<Modification>();
-      mods.add(new Modification(ModificationType.REPLACE,
-          Attributes.create(attr, String.valueOf(allowPreencoded))));
-
-      InternalClientConnection conn =
-           InternalClientConnection.getRootConnection();
-      ModifyOperation modifyOperation = conn.processModify(dn, mods);
-      assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
-
-      p = (PasswordPolicy) DirectoryServer.getAuthenticationPolicy(dn);
-      assertEquals(p.isAllowPreEncodedPasswords(), allowPreencoded);
-    } catch (Exception e) {
-      System.err.println("Failed to set ds-cfg-allow-pre-encoded-passwords " +
-                         " to " + allowPreencoded);
-      e.printStackTrace();
-      throw e;
-    }
-
-    return previousValue;
-  }
-
   /**
    * Retrieves a set of passwords (plain and variously hashed) that may
    * be used to test the different Unix "crypt" algorithms used by the Crypt
@@ -337,7 +138,6 @@
    *          may be used to test the different algorithms used by the Crypt
    *          password storage scheme.
    */
-
   @DataProvider(name = "testCryptPasswords")
   public Object[][] getTestCryptPasswords()
          throws Exception
@@ -361,38 +161,7 @@
           String plaintextPassword,
           String encodedPassword) throws Exception
   {
-      // Start/clear-out the memory backend
-    TestCaseUtils.initializeTestBackend(true);
-
-    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
-
-    try {
-
-      Entry userEntry = TestCaseUtils.makeEntry(
-       "dn: uid=testCrypt.user,o=test",
-       "objectClass: top",
-       "objectClass: person",
-       "objectClass: organizationalPerson",
-       "objectClass: inetOrgPerson",
-       "uid: testCrypt.user",
-       "givenName: TestCrypt",
-       "sn: User",
-       "cn: TestCrypt User",
-       "userPassword: " + encodedPassword);
-
-
-      // Add the entry
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=testCrypt.user,o=test",
-                  plaintextPassword),
-               "Failed to bind when pre-encoded password = \"" +
-               encodedPassword + "\" and " +
-               "plaintext password = \"" +
-               plaintextPassword + "\"" );
-    } finally {
-      setAllowPreencodedPasswords(allowPreencodedDefault);
-    }
+    testAuthPasswords("TestCrypt", plaintextPassword, encodedPassword);
   }
 
   /**
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PBKDF2PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PBKDF2PasswordStorageSchemeTestCase.java
index 6bf6828..0a39142 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PBKDF2PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PBKDF2PasswordStorageSchemeTestCase.java
@@ -29,11 +29,13 @@
 import org.opends.server.admin.std.meta.PBKDF2PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.PBKDF2PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
+import org.opends.server.types.DirectoryException;
 import org.testng.annotations.DataProvider;
 
 /**
  * A set of test cases for the PBKDF2 password storage scheme.
  */
+@SuppressWarnings("javadoc")
 public class PBKDF2PasswordStorageSchemeTestCase
        extends PasswordStorageSchemeTestCase
 {
@@ -84,4 +86,9 @@
     return scheme;
   }
 
+  @Override
+  protected String encodeOffline(final byte[] plaintextBytes) throws DirectoryException
+  {
+    return PBKDF2PasswordStorageScheme.encodeOffline(plaintextBytes);
+  }
 }
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageSchemeTestCase.java
index 6df2e8f..770057c 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PKCS5S2PasswordStorageSchemeTestCase.java
@@ -25,17 +25,14 @@
  */
 package org.opends.server.extensions;
 
-import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.server.AdminTestCaseUtils;
 import org.opends.server.admin.std.meta.PKCS5S2PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.PKCS5S2PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.types.Entry;
+import org.opends.server.types.DirectoryException;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
-import static org.testng.Assert.*;
-
 /**
  * A set of test cases for the PKCS5S2 password storage scheme.
  */
@@ -138,34 +135,13 @@
           String plaintextPassword,
           String encodedPassword) throws Exception
   {
-    // Start/clear-out the memory backend
-    TestCaseUtils.initializeTestBackend(true);
+    testAuthPasswords("TestPKCS5S2", plaintextPassword, encodedPassword);
+  }
 
-    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
-
-    try {
-      Entry userEntry = TestCaseUtils.makeEntry(
-       "dn: uid=testPKCS5S2.user,o=test",
-       "objectClass: top",
-       "objectClass: person",
-       "objectClass: organizationalPerson",
-       "objectClass: inetOrgPerson",
-       "uid: testPKCS5S2.user",
-       "givenName: TestPKCS5S2",
-       "sn: User",
-       "cn: TestPKCS5S2 User",
-       "userPassword: " + encodedPassword);
-
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=testPKCS5S2.user,o=test", plaintextPassword),
-               "Failed to bind when pre-encoded password = \"" +
-               encodedPassword + "\" and " +
-               "plaintext password = \"" +
-               plaintextPassword + "\"" );
-    } finally {
-      setAllowPreencodedPasswords(allowPreencodedDefault);
-    }
+  @Override
+  protected String encodeOffline(final byte[] plaintextBytes) throws DirectoryException
+  {
+    return PKCS5S2PasswordStorageScheme.encodeOffline(plaintextBytes);
   }
 
 }
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java
index eeb4745..61711f4 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/PasswordStorageSchemeTestCase.java
@@ -26,10 +26,6 @@
  */
 package org.opends.server.extensions;
 
-
-
-import static org.testng.Assert.*;
-
 import java.util.ArrayList;
 
 import org.opends.server.TestCaseUtils;
@@ -53,18 +49,22 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.testng.Assert.*;
 
 /**
  * A set of generic test cases for password storage schemes.
  */
+@SuppressWarnings("javadoc")
 public abstract class PasswordStorageSchemeTestCase
        extends ExtensionsTestCase
 {
-  // The configuration entry for this password storage scheme.
+  /** The configuration entry for this password storage scheme. */
   protected ConfigEntry configEntry;
 
-  // The string representation of the DN of the configuration entry for this
-  // password storage scheme.
+  /**
+   * The string representation of the DN of the configuration entry for this
+   * password storage scheme.
+   */
   private String configDNString;
 
 
@@ -91,9 +91,8 @@
    * Ensures that the Directory Server is started before running any of these
    * tests.
    */
-  @BeforeClass()
-  public void startServer()
-         throws Exception
+  @BeforeClass
+  public void startServer() throws Exception
   {
     TestCaseUtils.startServer();
 
@@ -115,6 +114,11 @@
   @DataProvider(name = "testPasswords")
   public Object[][] getTestPasswords()
   {
+    return getTestPasswordsStatic();
+  }
+
+  static Object[][] getTestPasswordsStatic()
+  {
     return new Object[][]
     {
       new Object[] { ByteString.empty() },
@@ -149,14 +153,18 @@
    * provided password, and ensures that the encoded value is correct.
    *
    * @param  plaintext  The plain-text version of the password to encode.
-   *
    * @throws  Exception  If an unexpected problem occurs.
    */
   @Test(dataProvider = "testPasswords")
   public void testStorageScheme(ByteString plaintext)
          throws Exception
   {
-    PasswordStorageScheme scheme = getScheme();
+    testStorageScheme(plaintext, getScheme());
+  }
+
+  static void testStorageScheme(ByteString plaintext,
+      PasswordStorageScheme<?> scheme) throws Exception
+  {
     assertNotNull(scheme);
     assertNotNull(scheme.getStorageSchemeName());
 
@@ -228,7 +236,7 @@
 
 
   @DataProvider
-  public Object[][] passwordsForBinding()
+  public static Object[][] passwordsForBinding()
   {
     return new Object[][]
     {
@@ -250,21 +258,23 @@
   @Test(dataProvider = "passwordsForBinding")
   public void testSettingEncodedPassword(ByteString plainPassword) throws Exception
   {
+    testSettingEncodedPassword(plainPassword, getScheme());
+  }
+
+  static void testSettingEncodedPassword(ByteString plainPassword,
+      PasswordStorageScheme<?> scheme) throws Exception, DirectoryException
+  {
     // Start/clear-out the memory backend
     TestCaseUtils.initializeTestBackend(true);
 
     boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
 
     try {
-      PasswordStorageScheme scheme = getScheme();
       ByteString schemeEncodedPassword =
            scheme.encodePasswordWithScheme(plainPassword);
 
-      //
       // This code creates a user with the encoded password,
       // and then verifies that they can bind with the raw password.
-      //
-
       Entry userEntry = TestCaseUtils.makeEntry(
            "dn: uid=test.user,o=test",
            "objectClass: top",
@@ -276,17 +286,16 @@
            "sn: User",
            "cn: Test User",
            "ds-privilege-name: bypass-acl",
-           "userPassword: " + schemeEncodedPassword.toString());
+           "userPassword: " + schemeEncodedPassword);
 
-      // Add the entry
       TestCaseUtils.addEntry(userEntry);
 
       assertTrue(TestCaseUtils.canBind("uid=test.user,o=test",
                  plainPassword.toString()),
                  "Failed to bind when pre-encoded password = \"" +
-                         schemeEncodedPassword.toString() + "\" and " +
+                         schemeEncodedPassword + "\" and " +
                          "plaintext password = \"" +
-                         plainPassword.toString() + "\"");
+                         plainPassword + "\"");
     } finally {
       setAllowPreencodedPasswords(allowPreencodedDefault);
     }
@@ -301,7 +310,7 @@
    * @param allowPreencoded whether or not to allow pre-encoded passwords
    * @return the previous value for the allow preencoded passwords
    */
-  protected boolean setAllowPreencodedPasswords(boolean allowPreencoded)
+  protected static boolean setAllowPreencodedPasswords(boolean allowPreencoded)
           throws Exception
   {
     // This code was borrowed from
@@ -335,14 +344,86 @@
     return previousValue;
   }
 
+  protected static void testAuthPasswords(final String upperName,
+      String plaintextPassword, String encodedPassword) throws Exception
+  {
+    // Start/clear-out the memory backend
+    TestCaseUtils.initializeTestBackend(true);
+
+    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
+
+    try
+    {
+      final String lowerName =
+          Character.toLowerCase(upperName.charAt(0)) + upperName.substring(1);
+
+      Entry userEntry = TestCaseUtils.makeEntry(
+          "dn: uid=" + lowerName + ".user,o=test",
+          "objectClass: top",
+          "objectClass: person",
+          "objectClass: organizationalPerson",
+          "objectClass: inetOrgPerson",
+          "uid: " + lowerName + ".user",
+          "givenName: " + upperName,
+          "sn: User",
+          "cn: " + upperName + " User",
+          "userPassword: " + encodedPassword);
+
+      TestCaseUtils.addEntry(userEntry);
+
+      assertTrue(TestCaseUtils.canBind(
+          "uid=" + lowerName + ".user,o=test", plaintextPassword),
+          "Failed to bind when pre-encoded password = \"" + encodedPassword
+          + "\" and " + "plaintext password = \"" + plaintextPassword + "\"");
+    }
+    finally
+    {
+      setAllowPreencodedPasswords(allowPreencodedDefault);
+    }
+  }
+
+  /**
+   * Tests the <CODE>encodeOffline</CODE> method.
+   *
+   * @param plaintext
+   *          The plaintext password to use for the test.
+   * @throws Exception
+   *           If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "testPasswords")
+  public void testEncodeOffline(ByteString plaintext) throws Exception
+  {
+    PasswordStorageScheme<?> scheme = getScheme();
+    String passwordString = encodeOffline(plaintext.toByteArray());
+    if (passwordString != null)
+    {
+      String[] pwComps = UserPasswordSyntax.decodeUserPassword(passwordString);
+      ByteString encodedPassword = ByteString.valueOf(pwComps[1]);
+
+      assertTrue(scheme.passwordMatches(plaintext, encodedPassword));
+    }
+  }
+
   /**
    * Retrieves an initialized instance of this password storage scheme.
    *
-   * @return  An initialized instance of this password storage scheme.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * @return An initialized instance of this password storage scheme.
+   * @throws Exception
+   *           If an unexpected problem occurs.
    */
-  protected abstract PasswordStorageScheme getScheme()
-         throws Exception;
+  protected abstract PasswordStorageScheme<?> getScheme() throws Exception;
+
+  /**
+   * Encodes the provided plaintext password while offline.
+   *
+   * @param plaintextBytes
+   *          The plaintext password in bytes to use for the test.
+   * @throws DirectoryException
+   *           If an unexpected problem occurs.
+   */
+  protected String encodeOffline(byte[] plaintextBytes) throws DirectoryException
+  {
+    return null;
+  }
 }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA1PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA1PasswordStorageSchemeTestCase.java
index c6485b1..ef8c9e4 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA1PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA1PasswordStorageSchemeTestCase.java
@@ -26,20 +26,11 @@
  */
 package org.opends.server.extensions;
 
-
-
-import org.testng.annotations.Test;
-
 import org.opends.server.admin.server.AdminTestCaseUtils;
 import org.opends.server.admin.std.meta.SaltedSHA1PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.SaltedSHA1PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.schema.UserPasswordSyntax;
-import org.forgerock.opendj.ldap.ByteString;
-
-import static org.testng.Assert.*;
-
-
+import org.opends.server.types.DirectoryException;
 
 /**
  * A set of test cases for the salted SHA-1 password storage scheme.
@@ -61,11 +52,10 @@
    * Retrieves an initialized instance of this password storage scheme.
    *
    * @return  An initialized instance of this password storage scheme.
-   *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  protected PasswordStorageScheme getScheme()
-         throws Exception
+  @Override
+  protected PasswordStorageScheme<?> getScheme() throws Exception
   {
     SaltedSHA1PasswordStorageScheme scheme =
          new SaltedSHA1PasswordStorageScheme();
@@ -80,35 +70,11 @@
     return scheme;
   }
 
-
-
-  /**
-   * Tests the <CODE>encodeOffline</CODE> method.
-   *
-   * @param  plaintext  The plaintext password to use for the test.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
-   */
-  @Test(dataProvider = "testPasswords")
-  public void testEncodeOffline(ByteString plaintext)
-         throws Exception
+  /** {@inheritDoc} */
+  @Override
+  protected String encodeOffline(final byte[] plaintextBytes) throws DirectoryException
   {
-    SaltedSHA1PasswordStorageScheme scheme =
-         new SaltedSHA1PasswordStorageScheme();
-
-    SaltedSHA1PasswordStorageSchemeCfg configuration =
-      AdminTestCaseUtils.getConfiguration(
-          SaltedSHA1PasswordStorageSchemeCfgDefn.getInstance(),
-          configEntry.getEntry()
-          );
-
-    scheme.initializePasswordStorageScheme(configuration);
-
-    String passwordString = SaltedSHA1PasswordStorageScheme.encodeOffline(plaintext.toByteArray());
-    String[] pwComps = UserPasswordSyntax.decodeUserPassword(passwordString);
-    ByteString encodedPassword = ByteString.valueOf(pwComps[1]);
-
-    assertTrue(scheme.passwordMatches(plaintext, encodedPassword));
+    return SaltedSHA1PasswordStorageScheme.encodeOffline(plaintextBytes);
   }
 }
 
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA256PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA256PasswordStorageSchemeTestCase.java
index 3760cbd..b774b63 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA256PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA256PasswordStorageSchemeTestCase.java
@@ -22,28 +22,21 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2010 ForgeRock AS.
+ *      Portions Copyright 2010-2014 ForgeRock AS.
  */
 package org.opends.server.extensions;
 
-
-import static org.testng.Assert.*;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import org.opends.server.TestCaseUtils;
 import org.opends.server.admin.server.AdminTestCaseUtils;
-import org.opends.server.admin.std.meta.
-            SaltedSHA256PasswordStorageSchemeCfgDefn;
+import org.opends.server.admin.std.meta.SaltedSHA256PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.SaltedSHA256PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.types.Entry;
-
-
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
 
 /**
  * A set of test cases for the salted SHA-256 password storage scheme.
  */
+@SuppressWarnings("javadoc")
 public class SaltedSHA256PasswordStorageSchemeTestCase
        extends PasswordStorageSchemeTestCase
 {
@@ -64,8 +57,8 @@
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  protected PasswordStorageScheme getScheme()
-         throws Exception
+  @Override
+  protected PasswordStorageScheme<?> getScheme() throws Exception
   {
     SaltedSHA256PasswordStorageScheme scheme =
          new SaltedSHA256PasswordStorageScheme();
@@ -89,10 +82,8 @@
    * @return  A set of couple (cleartext, encrypted) passwords that
    *          may be used to test the SSHA256 password storage scheme
    */
-
   @DataProvider(name = "testSSHA256Passwords")
-  public Object[][] getTestSSHA256Passwords()
-         throws Exception
+  public Object[][] getTestSSHA256Passwords() throws Exception
   {
     return new Object[][]
     {
@@ -105,38 +96,7 @@
           String plaintextPassword,
           String encodedPassword) throws Exception
   {
-    // Start/clear-out the memory backend
-    TestCaseUtils.initializeTestBackend(true);
-
-    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
-
-    try {
-
-      Entry userEntry = TestCaseUtils.makeEntry(
-       "dn: uid=testSSHA256.user,o=test",
-       "objectClass: top",
-       "objectClass: person",
-       "objectClass: organizationalPerson",
-       "objectClass: inetOrgPerson",
-       "uid: testSSHA256.user",
-       "givenName: TestSSHA256",
-       "sn: User",
-       "cn: TestSSHA256 User",
-       "userPassword: " + encodedPassword);
-
-
-      // Add the entry
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=testSSHA256.user,o=test",
-                  plaintextPassword),
-               "Failed to bind when pre-encoded password = \"" +
-               encodedPassword + "\" and " +
-               "plaintext password = \"" +
-               plaintextPassword + "\"" );
-    } finally {
-      setAllowPreencodedPasswords(allowPreencodedDefault);
-    }
+    testAuthPasswords("TestSSHA256", plaintextPassword, encodedPassword);
   }
 
 }
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA384PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA384PasswordStorageSchemeTestCase.java
index 162a1e8..46d78a0 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA384PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA384PasswordStorageSchemeTestCase.java
@@ -22,29 +22,21 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2010 ForgeRock AS.
+ *      Portions Copyright 2010-2014 ForgeRock AS.
  */
 package org.opends.server.extensions;
 
-
-import static org.testng.Assert.*;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import org.opends.server.TestCaseUtils;
-
 import org.opends.server.admin.server.AdminTestCaseUtils;
-import org.opends.server.admin.std.meta.
-            SaltedSHA384PasswordStorageSchemeCfgDefn;
+import org.opends.server.admin.std.meta.SaltedSHA384PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.SaltedSHA384PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.types.Entry;
-
-
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
 
 /**
  * A set of test cases for the salted SHA-384 password storage scheme.
  */
+@SuppressWarnings("javadoc")
 public class SaltedSHA384PasswordStorageSchemeTestCase
        extends PasswordStorageSchemeTestCase
 {
@@ -65,8 +57,8 @@
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  protected PasswordStorageScheme getScheme()
-         throws Exception
+  @Override
+  protected PasswordStorageScheme<?> getScheme() throws Exception
   {
     SaltedSHA384PasswordStorageScheme scheme =
          new SaltedSHA384PasswordStorageScheme();
@@ -90,10 +82,8 @@
    * @return  A set of couple (cleartext, encrypted) passwords that
    *          may be used to test the SSHA384 password storage scheme
    */
-
   @DataProvider(name = "testSSHA384Passwords")
-  public Object[][] getTestSSHA384Passwords()
-         throws Exception
+  public Object[][] getTestSSHA384Passwords() throws Exception
   {
     return new Object[][]
     {
@@ -108,38 +98,7 @@
           String plaintextPassword,
           String encodedPassword) throws Exception
   {
-    // Start/clear-out the memory backend
-    TestCaseUtils.initializeTestBackend(true);
-
-    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
-
-    try {
-
-      Entry userEntry = TestCaseUtils.makeEntry(
-       "dn: uid=testSSHA384.user,o=test",
-       "objectClass: top",
-       "objectClass: person",
-       "objectClass: organizationalPerson",
-       "objectClass: inetOrgPerson",
-       "uid: testSSHA384.user",
-       "givenName: TestSSHA384",
-       "sn: User",
-       "cn: TestSSHA384 User",
-       "userPassword: " + encodedPassword);
-
-
-      // Add the entry
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=testSSHA384.user,o=test",
-                  plaintextPassword),
-               "Failed to bind when pre-encoded password = \"" +
-               encodedPassword + "\" and " +
-               "plaintext password = \"" +
-               plaintextPassword + "\"" );
-    } finally {
-      setAllowPreencodedPasswords(allowPreencodedDefault);
-    }
+    testAuthPasswords("TestSSHA384", plaintextPassword, encodedPassword);
   }
 
 }
diff --git a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA512PasswordStorageSchemeTestCase.java b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA512PasswordStorageSchemeTestCase.java
index 7e909f8..bc3ad82 100644
--- a/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA512PasswordStorageSchemeTestCase.java
+++ b/opendj3-server-dev/tests/unit-tests-testng/src/server/org/opends/server/extensions/SaltedSHA512PasswordStorageSchemeTestCase.java
@@ -22,29 +22,22 @@
  *
  *
  *      Copyright 2006-2008 Sun Microsystems, Inc.
- *      Portions Copyright 2010 ForgeRock AS.
+ *      Portions Copyright 2010-2014 ForgeRock AS.
  */
 package org.opends.server.extensions;
 
-
-import static org.testng.Assert.*;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import org.opends.server.TestCaseUtils;
-
 import org.opends.server.admin.server.AdminTestCaseUtils;
-import org.opends.server.admin.std.meta.
-            SaltedSHA512PasswordStorageSchemeCfgDefn;
+import org.opends.server.admin.std.meta.SaltedSHA512PasswordStorageSchemeCfgDefn;
 import org.opends.server.admin.std.server.SaltedSHA512PasswordStorageSchemeCfg;
 import org.opends.server.api.PasswordStorageScheme;
-import org.opends.server.types.Entry;
-
-
+import org.opends.server.types.DirectoryException;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
 
 /**
  * A set of test cases for the salted SHA-512 password storage scheme.
  */
+@SuppressWarnings("javadoc")
 public class SaltedSHA512PasswordStorageSchemeTestCase
        extends PasswordStorageSchemeTestCase
 {
@@ -65,8 +58,8 @@
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  protected PasswordStorageScheme getScheme()
-         throws Exception
+  @Override
+  protected PasswordStorageScheme<?> getScheme() throws Exception
   {
     SaltedSHA512PasswordStorageScheme scheme =
          new SaltedSHA512PasswordStorageScheme();
@@ -106,39 +99,13 @@
           String plaintextPassword,
           String encodedPassword) throws Exception
   {
-    // Start/clear-out the memory backend
-    TestCaseUtils.initializeTestBackend(true);
-
-    boolean allowPreencodedDefault = setAllowPreencodedPasswords(true);
-
-    try {
-
-      Entry userEntry = TestCaseUtils.makeEntry(
-       "dn: uid=testSSHA512.user,o=test",
-       "objectClass: top",
-       "objectClass: person",
-       "objectClass: organizationalPerson",
-       "objectClass: inetOrgPerson",
-       "uid: testSSHA512.user",
-       "givenName: TestSSHA512",
-       "sn: User",
-       "cn: TestSSHA512 User",
-       "userPassword: " + encodedPassword);
-
-
-      // Add the entry
-      TestCaseUtils.addEntry(userEntry);
-
-      assertTrue(TestCaseUtils.canBind("uid=testSSHA512.user,o=test",
-                  plaintextPassword),
-               "Failed to bind when pre-encoded password = \"" +
-               encodedPassword + "\" and " +
-               "plaintext password = \"" +
-               plaintextPassword + "\"" );
-    } finally {
-      setAllowPreencodedPasswords(allowPreencodedDefault);
-    }
+    testAuthPasswords("TestSSHA512", plaintextPassword, encodedPassword);
   }
 
+  /** {@inheritDoc} */
+  @Override
+  protected String encodeOffline(byte[] plaintextBytes) throws DirectoryException
+  {
+    return SaltedSHA512PasswordStorageScheme.encodeOffline(plaintextBytes);
+  }
 }
-

--
Gitblit v1.10.0