db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r1227150 - in /db/derby/code/trunk/java: engine/org/apache/derby/catalog/ engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/impl/jdbc/authentication/ engine/org/apache/derby/impl/sql/catalog/ engine/org/apache/derby/loc/ ...
Date Wed, 04 Jan 2012 13:30:22 GMT
Author: rhillegas
Date: Wed Jan  4 13:30:22 2012
New Revision: 1227150

URL: http://svn.apache.org/viewvc?rev=1227150&view=rev
Log:
DERBY-866: Wire the new NATIVE password-management procedures to the recently checked-in password hashing improvements.

Added:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java   (with props)
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/BasicAuthenticationServiceImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthProcs.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/upgradeTests/Changes10_9.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/catalog/SystemProcedures.java Wed Jan  4 13:30:22 2012
@@ -66,6 +66,7 @@ import org.apache.derby.iapi.sql.diction
 import org.apache.derby.iapi.sql.dictionary.SystemColumn;
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
 import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
 import org.apache.derby.iapi.sql.dictionary.UserDescriptor;
@@ -2066,7 +2067,7 @@ public class SystemProcedures  {
             */
             dd.startWriting(lcc);
 
-            UserDescriptor  userDescriptor = makeUserDescriptor( lcc, userName, password.toCharArray() );
+            UserDescriptor  userDescriptor = makeUserDescriptor( lcc, userName, password );
 
             dd.addDescriptor( userDescriptor, null, DataDictionary.SYSUSERS_CATALOG_NUM, false, tc );
             
@@ -2076,24 +2077,26 @@ public class SystemProcedures  {
         (
          LanguageConnectionContext lcc,
          String userName,
-         char[] password
+         String password
          )
         throws StandardException
     {
         DataDictionary dd = lcc.getDataDictionary();
         DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
+        PasswordHasher hasher = dd.makePasswordHasher( lcc.getTransactionExecute().getProperties() );
+
+        if ( hasher == null )
+        {
+            throw StandardException.newException( SQLState.WEAK_AUTHENTICATION );
+        }
 
-        //
-        // FIXME: DERBY-866 Proper values need to be computed for the
-        // following variables once Knut is done reworking builtin hashing.
-        //
-        String  hashingScheme = "???????";
-        char[]  hashedPassword = password;
+        String  hashingScheme = hasher.encodeHashingScheme();
+        String  hashedPassword = hasher.hashPasswordIntoString( userName, password );
             
         Timestamp   currentTimestamp = new Timestamp( (new java.util.Date()).getTime() );
 
         UserDescriptor  userDescriptor = ddg.newUserDescriptor
-            ( userName, hashingScheme, hashedPassword, currentTimestamp );
+            ( userName, hashingScheme, hashedPassword.toCharArray(), currentTimestamp );
 
         return userDescriptor;
     }
@@ -2124,7 +2127,7 @@ public class SystemProcedures  {
             */
             dd.startWriting(lcc);
 
-            UserDescriptor  userDescriptor = makeUserDescriptor( lcc, userName, password.toCharArray() );
+            UserDescriptor  userDescriptor = makeUserDescriptor( lcc, userName, password );
 
             dd.updateUser( userDescriptor, tc );
             

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/DataDictionary.java Wed Jan  4 13:30:22 2012
@@ -41,6 +41,7 @@ import org.apache.derby.catalog.UUID;
 import org.apache.derby.iapi.services.uuid.UUIDFactory;
 
 import java.sql.Types;
+import java.util.Dictionary;
 import java.util.List;
 import java.util.Hashtable;
 
@@ -402,6 +403,15 @@ public interface DataDictionary
 			throws StandardException;
 
 	/**
+	 * Get the default password hasher for this database level. Returns null
+     * if the system is at rev level 10.5 or earlier.
+	 *
+	 * @param props   The persistent properties used to configure password hashing.
+	 */
+    public  PasswordHasher  makePasswordHasher( Dictionary props )
+        throws StandardException;
+    
+	/**
 	 * Get the descriptor for the system schema. Schema descriptors include 
      * authorization ids and schema ids.
      *

Added: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java?rev=1227150&view=auto
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java (added)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java Wed Jan  4 13:30:22 2012
@@ -0,0 +1,260 @@
+/*
+
+   Derby - Class org.apache.derby.iapi.sql.dictionary.PasswordHasher
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to you under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+ */
+package org.apache.derby.iapi.sql.dictionary;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.services.sanity.SanityManager;
+import org.apache.derby.iapi.util.StringUtil;
+import org.apache.derby.shared.common.reference.SQLState;
+
+/**
+ * <p>
+ * This machine performs the hashing of Derby passwords.
+ * </p>
+ */
+public  class   PasswordHasher
+{
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTANTS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+     /**
+     * The encoding to use when converting the credentials to a byte array
+     * that can be passed to the hash function in the configurable hash scheme.
+     */
+    private static final String ENCODING = "UTF-8";
+
+    /**
+     * Pattern that is prefixed to the stored password in the SHA-1
+     * authentication scheme.
+     */
+    public static final String ID_PATTERN_SHA1_SCHEME = "3b60";
+
+    /**
+     * Pattern that is prefixed to the stored password in the configurable
+     * hash authentication scheme.
+     */
+    public static final String ID_PATTERN_CONFIGURABLE_HASH_SCHEME = "3b61";
+
+    /**
+     * Pattern that is prefixed to the stored password in the configurable
+     * hash authentication scheme if key stretching has been applied. This
+     * scheme extends the configurable hash scheme by adding a random salt and
+     * applying the hash function multiple times when generating the hashed
+     * token.
+     */
+    public static final String
+            ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME = "3b62";
+
+    /**
+     * Character that separates the hash value from the name of the hash
+     * algorithm in the stored password generated by the configurable hash
+     * authentication scheme.
+     */
+    private static final char SEPARATOR_CHAR = ':';
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // STATE
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    private String  _messageDigestAlgorithm;
+    private byte[]  _salt;  // can be null
+    private int         _iterations;
+    
+
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // CONSTRUCTORS
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Construct from pieces. Used for databases at rev level 10.6 or later.
+     * </p>
+     */
+    public  PasswordHasher
+        (
+         String messageDigestAlgorithm,
+         byte[]   salt,
+         int    iterations
+         )
+    {
+        _messageDigestAlgorithm = messageDigestAlgorithm;
+        _salt = salt;
+        _iterations = iterations;
+    }
+
+    /**
+     * <p>
+     * Construct from a hashed BUILTIN password stored in the PropertyConglomerate
+     * or from a SYSUSERS.HASHINGSCHEME column.
+     * </p>
+     */
+    public  PasswordHasher( String hashingScheme )
+    {
+        if ( hashingScheme.startsWith( ID_PATTERN_CONFIGURABLE_HASH_SCHEME) )
+        {
+            _messageDigestAlgorithm = hashingScheme.substring
+                ( hashingScheme.indexOf( SEPARATOR_CHAR) +  1);
+            _salt = null;
+            _iterations = 1;
+        }
+        else if ( hashingScheme.startsWith( ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME) )
+        {
+            int saltPos = hashingScheme.indexOf(SEPARATOR_CHAR) + 1;
+            int iterPos = hashingScheme.indexOf(SEPARATOR_CHAR, saltPos) + 1;
+            int algoPos = hashingScheme.indexOf(SEPARATOR_CHAR, iterPos) + 1;
+
+            _salt = StringUtil.fromHexString
+                ( hashingScheme, saltPos, iterPos - saltPos - 1 );
+            _iterations = Integer.parseInt
+                ( hashingScheme.substring(iterPos, algoPos - 1) );
+            _messageDigestAlgorithm = hashingScheme.substring(algoPos);
+        }
+        else
+        {
+            if (SanityManager.DEBUG)
+            {
+                SanityManager.THROWASSERT
+                    (
+                        "Unknown authentication scheme for token " +
+                        hashingScheme
+                     );
+            }
+        }
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////////////
+    //
+    // PUBLIC BEHAVIOR
+    //
+    ///////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * <p>
+     * Produce a hashed password using a plaintext username and password. Turn it into
+     * a printable string.
+     * </p>
+     */
+    public  String  hashPasswordIntoString( String userName, String password )
+        throws StandardException
+    {
+        if (password == null) {
+            return null;
+        }
+
+        byte[] userBytes;
+        byte[] passwordBytes;
+        try {
+            userBytes = userName.getBytes(ENCODING);
+            passwordBytes = password.getBytes(ENCODING);
+        } catch (UnsupportedEncodingException uee) {
+            // UTF-8 should always be available, so this should never happen.
+            throw StandardException.plainWrapException(uee);
+        }
+
+        MessageDigest md = getEmptyMessageDigest();
+
+        byte[] digest = null;
+        for (int i = 0; i < _iterations; i++)
+        {
+            md.reset();
+            if (digest != null) {
+                md.update(digest);
+            }
+            md.update(userBytes);
+            md.update(passwordBytes);
+            if ( _salt != null) {
+                md.update( _salt );
+            }
+            digest = md.digest();
+        }
+
+        return StringUtil.toHexString( digest, 0, digest.length );
+    }
+    private MessageDigest   getEmptyMessageDigest()
+        throws StandardException
+    {
+        if ( _messageDigestAlgorithm == null ) { throw badMessageDigest( null ); }
+        try {
+            return MessageDigest.getInstance( _messageDigestAlgorithm );
+        } catch (NoSuchAlgorithmException nsae) { throw badMessageDigest( nsae ); }
+    }
+    private StandardException   badMessageDigest( Throwable t )
+    {
+        String  digestName = (_messageDigestAlgorithm == null) ? "NULL" : _messageDigestAlgorithm;
+
+        return StandardException.newException( SQLState.DIGEST_NO_SUCH_ALGORITHM, t, digestName );
+    }
+
+    /**
+     * <p>
+     * Encodes the hashing algorithm in a string suitable for storing in SYSUSERS.HASHINGSCHEME.
+     * </p>
+     */
+    public  String  encodeHashingScheme( )
+    {
+        return hashAndEncode( "" );
+    }
+    
+    /**
+     * <p>
+     * Hash a username/password pair and return an encoded representation suitable
+     * for storing as a BUILTIN password value in the PropertyConglomerate.
+     * </p>
+     */
+    public  String  hashAndEncode( String userName, String password )
+        throws StandardException
+    {
+        String  stringDigest = hashPasswordIntoString( userName, password );
+
+        return hashAndEncode( stringDigest );
+    }
+    private String  hashAndEncode( String stringDigest )
+    {
+        if (( _salt == null || _salt.length == 0) && _iterations == 1)
+        {
+            // No salt was used, and only a single iteration, which is
+            // identical to the default hashing scheme in 10.6-10.8. Generate
+            // a token on a format compatible with those old versions.
+            return ID_PATTERN_CONFIGURABLE_HASH_SCHEME +
+                stringDigest +
+                SEPARATOR_CHAR + _messageDigestAlgorithm;
+        } else {
+            // Salt and/or multiple iterations was used, so we need to add
+            // those parameters to the token in order to verify the credentials
+            // later.
+            return ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME +
+                stringDigest +
+                SEPARATOR_CHAR + StringUtil.toHexString( _salt, 0, _salt.length) +
+                SEPARATOR_CHAR + _iterations + SEPARATOR_CHAR + _messageDigestAlgorithm;
+        }
+    }
+}

Propchange: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/PasswordHasher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/AuthenticationServiceBase.java Wed Jan  4 13:30:22 2012
@@ -49,6 +49,7 @@ import org.apache.derby.iapi.util.String
 
 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -112,9 +113,9 @@ import org.apache.derby.iapi.reference.S
  * a user's credentials with those stored in the database, the stored password
  * is prefixed with an identifier that tells which scheme is being used.
  * Passwords stored using the SHA-1 authentication scheme are prefixed with
- * {@link #ID_PATTERN_SHA1_SCHEME}. Passwords that are stored using the
+ * {@link PasswordHasher#ID_PATTERN_SHA1_SCHEME}. Passwords that are stored using the
  * configurable hash authentication scheme are prefixed with
- * {@link #ID_PATTERN_CONFIGURABLE_HASH_SCHEME} and suffixed with the name of
+ * {@link PasswordHasher#ID_PATTERN_CONFIGURABLE_HASH_SCHEME} and suffixed with the name of
  * the message digest algorithm.
  * </p>
  */
@@ -133,45 +134,10 @@ public abstract class AuthenticationServ
 						SanityManager.DEBUG ? "AuthenticationTrace" : null;
 
     /**
-     * Pattern that is prefixed to the stored password in the SHA-1
-     * authentication scheme.
-     */
-    public static final String ID_PATTERN_SHA1_SCHEME = "3b60";
-
-    /**
-     * Pattern that is prefixed to the stored password in the configurable
-     * hash authentication scheme.
-     */
-    public static final String ID_PATTERN_CONFIGURABLE_HASH_SCHEME = "3b61";
-
-    /**
-     * Pattern that is prefixed to the stored password in the configurable
-     * hash authentication scheme if key stretching has been applied. This
-     * scheme extends the configurable hash scheme by adding a random salt and
-     * applying the hash function multiple times when generating the hashed
-     * token.
-     */
-    public static final String
-            ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME = "3b62";
-
-    /**
         Userid with Strong password substitute DRDA security mechanism
     */
     protected static final int SECMEC_USRSSBPWD = 8;
 
-    /**
-     * The encoding to use when converting the credentials to a byte array
-     * that can be passed to the hash function in the configurable hash scheme.
-     */
-    private static final String ENCODING = "UTF-8";
-
-    /**
-     * Character that separates the hash value from the name of the hash
-     * algorithm in the stored password generated by the configurable hash
-     * authentication scheme.
-     */
-    static final char SEPARATOR_CHAR = ':';
-
 	//
 	// constructor
 	//
@@ -482,7 +448,7 @@ public abstract class AuthenticationServ
         bytePasswd = toHexByte(plainTxtUserPassword);
 		algorithm.update(bytePasswd);
 		byte[] encryptVal = algorithm.digest();
-        String hexString = ID_PATTERN_SHA1_SCHEME +
+        String hexString = PasswordHasher.ID_PATTERN_SHA1_SCHEME +
                 StringUtil.toHexString(encryptVal,0,encryptVal.length);
 		return (hexString);
 
@@ -531,90 +497,6 @@ public abstract class AuthenticationServ
 
     /**
      * <p>
-     * Hash credentials using the specified hash algorithm, possibly performing
-     * key stretching by adding random salt and applying the hash function
-     * multiple times.
-     * </p>
-     *
-     * <p>
-     * The algorithm must be supported by one of the registered security
-     * providers in the JVM.
-     * </p>
-     *
-     * <p>
-     * This method is only used by the configurable hash authentication scheme.
-     * </p>
-     *
-     * @param user the user whose password to encrypt
-     * @param password the plain text password
-     * @param algorithm the hash algorithm to use
-     * @param salt random salt to add to the credentials (possibly {@code null})
-     * @param iterations the number of times to apply the hash function
-     * @return a digest of the user name and password formatted as a string,
-     *         or {@code null} if {@code password} is {@code null}
-     * @throws StandardException if the specified algorithm is not supported
-     */
-    String encryptPasswordConfigurableScheme(
-            String user, String password, String algorithm,
-            byte[] salt, int iterations)
-            throws StandardException
-    {
-        if (password == null) {
-            return null;
-        }
-
-        byte[] userBytes;
-        byte[] passwordBytes;
-        try {
-            userBytes = user.getBytes(ENCODING);
-            passwordBytes = password.getBytes(ENCODING);
-        } catch (UnsupportedEncodingException uee) {
-            // UTF-8 should always be available, so this should never happen.
-            throw StandardException.plainWrapException(uee);
-        }
-
-        MessageDigest md;
-        try {
-            md = MessageDigest.getInstance(algorithm);
-        } catch (NoSuchAlgorithmException nsae) {
-            throw StandardException.newException(
-                    SQLState.DIGEST_NO_SUCH_ALGORITHM, nsae, algorithm);
-        }
-
-        byte[] digest = null;
-        for (int i = 0; i < iterations; i++) {
-            md.reset();
-            if (digest != null) {
-                md.update(digest);
-            }
-            md.update(userBytes);
-            md.update(passwordBytes);
-            if (salt != null) {
-                md.update(salt);
-            }
-            digest = md.digest();
-        }
-
-        if ((salt == null || salt.length == 0) && iterations == 1) {
-            // No salt was used, and only a single iteration, which is
-            // identical to the default hashing scheme in 10.6-10.8. Generate
-            // a token on a format compatible with those old versions.
-            return ID_PATTERN_CONFIGURABLE_HASH_SCHEME +
-                StringUtil.toHexString(digest, 0, digest.length) +
-                SEPARATOR_CHAR + algorithm;
-        } else {
-            // Salt and/or multiple iterations was used, so we need to add
-            // those parameters to the token in order to verify the credentials
-            // later.
-            return ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME +
-                StringUtil.toHexString(digest, 0, digest.length) +
-                SEPARATOR_CHAR + StringUtil.toHexString(salt, 0, salt.length) +
-                SEPARATOR_CHAR + iterations + SEPARATOR_CHAR + algorithm;
-        }
-    }
-
-    /**
-     * <p>
      * Encrypt a password using the default hash algorithm for this system
      * before it's stored in the database.
      * </p>
@@ -639,100 +521,14 @@ public abstract class AuthenticationServ
     private String encryptUsingDefaultAlgorithm(String user,
                                                 String password,
                                                 Dictionary props)
-            throws StandardException {
-        DataDictionary dd = getDataDictionary();
-
-        // Support for configurable hash algorithm was added in Derby 10.6, so
-        // we don't want to store a hash using the new scheme if the database
-        // is running in soft upgrade and may be used with an older version
-        // later.
-        boolean supportConfigurableHash =
-                dd.checkVersion(DataDictionary.DD_VERSION_DERBY_10_6, null);
-
-        // Support for key stretching was added in Derby 10.9, so don't use it
-        // if the database may still be used with an older version.
-        boolean supportKeyStretching =
-                dd.checkVersion(DataDictionary.DD_VERSION_DERBY_10_9, null);
-
-        if (supportConfigurableHash) {
-            String algorithm = (String)
-                    PropertyUtil.getPropertyFromSet(
-                        props,
-                        Property.AUTHENTICATION_BUILTIN_ALGORITHM);
-
-            if (algorithm != null && algorithm.length() > 0) {
-                byte[] salt = null;
-                int iterations = 1;
-
-                if (supportKeyStretching) {
-                    salt = generateRandomSalt(props);
-                    iterations = getIntProperty(
-                            props,
-                            Property.AUTHENTICATION_BUILTIN_ITERATIONS,
-                            Property.AUTHENTICATION_BUILTIN_ITERATIONS_DEFAULT,
-                            1, Integer.MAX_VALUE);
-                }
-
-                return encryptPasswordConfigurableScheme(
-                        user, password, algorithm, salt, iterations);
-            }
-        }
-
-        return encryptPasswordSHA1Scheme(password);
-    }
-
-    /**
-     * Get the value of an integer property.
-     *
-     * @param props database properties
-     * @param key the key of the property
-     * @param defaultValue which value to return if the property is not set,
-     *   or if the property value is not in the valid range
-     * @param minValue lowest property value to accept
-     * @param maxValue highest property value to accept
-     * @return the value of the property
-     */
-    private int getIntProperty(
-            Dictionary props, String key,
-            int defaultValue, int minValue, int maxValue) {
-
-        String sVal = (String) PropertyUtil.getPropertyFromSet(props, key);
-
-        if (sVal != null) {
-            try {
-                int i = Integer.parseInt(sVal);
-                if (i >= minValue && i <= maxValue) {
-                    return i;
-                }
-            } catch (NumberFormatException nfe) {
-                // By convention, Derby ignores property values that cannot be
-                // parsed. Use the default value instead.
-            }
-        }
-
-        return defaultValue;
-    }
+            throws StandardException
+    {
+        if ( password ==  null ) { return null; }
 
-    /**
-     * Generate an array of random bytes to use as salt when hashing
-     * credentials.
-     *
-     * @param props database properties that possibly specify the desired
-     *   length of the salt
-     * @return random bytes
-     */
-    private byte[] generateRandomSalt(Dictionary props) {
-        int saltLength = getIntProperty(
-                props,
-                Property.AUTHENTICATION_BUILTIN_SALT_LENGTH,
-                Property.AUTHENTICATION_BUILTIN_SALT_LENGTH_DEFAULT,
-                0, Integer.MAX_VALUE);
-
-        SecureRandom random = new SecureRandom();
-        byte[] salt = new byte[saltLength];
-        random.nextBytes(salt);
+        PasswordHasher  hasher = getDataDictionary().makePasswordHasher( props );
 
-        return salt;
+        if ( hasher != null ) { return hasher.hashAndEncode( user, password ); }
+        else { return encryptPasswordSHA1Scheme(password); }
     }
 
     /**
@@ -761,10 +557,10 @@ public abstract class AuthenticationServ
      * the one passed-in.
      *
      * The substitution algorithm used is the same as the one used in the
-     * SHA-1 authentication scheme ({@link #ID_PATTERN_SHA1_SCHEME}), so in
+     * SHA-1 authentication scheme ({@link PasswordHasher#ID_PATTERN_SHA1_SCHEME}), so in
      * the case of database passwords stored using that scheme, we can simply
      * compare the received hash with the stored hash. If the configurable
-     * hash authentication scheme {@link #ID_PATTERN_CONFIGURABLE_HASH_SCHEME}
+     * hash authentication scheme {@link PasswordHasher#ID_PATTERN_CONFIGURABLE_HASH_SCHEME}
      * is used, we have no way to find out if the received hash matches the
      * stored password, since we cannot decrypt the hashed passwords and
      * re-apply another hash algorithm. Therefore, strong password substitution
@@ -857,7 +653,7 @@ public abstract class AuthenticationServ
             bytePasswd = toHexByte(password);
             messageDigest.update(bytePasswd);
             byte[] encryptVal = messageDigest.digest();
-            hexString = ID_PATTERN_SHA1_SCHEME +
+            hexString = PasswordHasher.ID_PATTERN_SHA1_SCHEME +
                 StringUtil.toHexString(encryptVal, 0, encryptVal.length);
         }
         else

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/BasicAuthenticationServiceImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/BasicAuthenticationServiceImpl.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/BasicAuthenticationServiceImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/authentication/BasicAuthenticationServiceImpl.java Wed Jan  4 13:30:22 2012
@@ -21,6 +21,7 @@
 
 package org.apache.derby.impl.jdbc.authentication;
 
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
 import org.apache.derby.iapi.reference.Attribute;
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.authentication.UserAuthenticator;
@@ -270,34 +271,14 @@ public final class BasicAuthenticationSe
                 String user, String password, String storedPassword)
             throws StandardException
     {
-        if (storedPassword.startsWith(ID_PATTERN_SHA1_SCHEME)) {
+        if (storedPassword.startsWith( PasswordHasher.ID_PATTERN_SHA1_SCHEME )) {
             return encryptPasswordSHA1Scheme(password);
-        } else if (storedPassword.startsWith(
-                        ID_PATTERN_CONFIGURABLE_HASH_SCHEME)) {
-            String algorithm = storedPassword.substring(
-                    storedPassword.indexOf(SEPARATOR_CHAR) + 1);
-            return encryptPasswordConfigurableScheme(
-                    user, password, algorithm, null, 1);
-        } else if (storedPassword.startsWith(
-                        ID_PATTERN_CONFIGURABLE_STRETCHED_SCHEME)) {
-            int saltPos = storedPassword.indexOf(SEPARATOR_CHAR) + 1;
-            int iterPos = storedPassword.indexOf(SEPARATOR_CHAR, saltPos) + 1;
-            int algoPos = storedPassword.indexOf(SEPARATOR_CHAR, iterPos) + 1;
-
-            byte[] salt = StringUtil.fromHexString(
-                    storedPassword, saltPos, iterPos - saltPos - 1);
-            int iterations = Integer.parseInt(
-                    storedPassword.substring(iterPos, algoPos - 1));
-            String algorithm = storedPassword.substring(algoPos);
-            return encryptPasswordConfigurableScheme(
-                    user, password, algorithm, salt, iterations);
-        } else {
-            if (SanityManager.DEBUG) {
-                SanityManager.THROWASSERT(
-                        "Unknown authentication scheme for token " +
-                        storedPassword);
-            }
-            return null;
+        }
+        else
+        {
+            PasswordHasher  hasher = new PasswordHasher( storedPassword );
+
+            return hasher.hashAndEncode( user, password );
         }
     }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/catalog/DataDictionaryImpl.java Wed Jan  4 13:30:22 2012
@@ -46,6 +46,7 @@ import org.apache.derby.iapi.sql.diction
 import org.apache.derby.iapi.sql.dictionary.DependencyDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ForeignKeyConstraintDescriptor;
 import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
 import org.apache.derby.iapi.sql.dictionary.TupleDescriptor;
 import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
 import org.apache.derby.iapi.sql.dictionary.KeyConstraintDescriptor;
@@ -153,10 +154,13 @@ import org.apache.derby.iapi.services.lo
 import org.apache.derby.iapi.services.locks.ShExQual;
 import org.apache.derby.iapi.util.IdUtil;
 
+import java.security.SecureRandom;
+
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Dictionary;
 import java.util.GregorianCalendar;
 import java.util.Hashtable;
 import java.util.HashMap;
@@ -1459,6 +1463,104 @@ public final class	DataDictionaryImpl
                 SchemaDescriptor.SYSCS_UTIL_SCHEMA_UUID);
     }
 
+    // returns null if database is at rev level 10.5 or earlier
+    public  PasswordHasher  makePasswordHasher( Dictionary props )
+        throws StandardException
+    {
+        // Support for configurable hash algorithm was added in Derby 10.6, so
+        // we don't want to store a hash using the new scheme if the database
+        // is running in soft upgrade and may be used with an older version
+        // later.
+        boolean supportConfigurableHash = checkVersion(DataDictionary.DD_VERSION_DERBY_10_6, null);
+
+        // Support for key stretching was added in Derby 10.9, so don't use it
+        // if the database may still be used with an older version.
+        boolean supportKeyStretching = checkVersion(DataDictionary.DD_VERSION_DERBY_10_9, null);
+
+        if ( !supportConfigurableHash ) { return null; }
+        else
+        {
+            String algorithm = (String)
+                    PropertyUtil.getPropertyFromSet(
+                        props,
+                        Property.AUTHENTICATION_BUILTIN_ALGORITHM);
+
+            if ( algorithm == null ) { return null; }
+
+            byte[] salt = null;
+            int iterations = 1;
+            
+            if (algorithm != null && algorithm.length() > 0) {
+
+                if (supportKeyStretching) {
+                    salt = generateRandomSalt(props);
+                    iterations = getIntProperty(
+                            props,
+                            Property.AUTHENTICATION_BUILTIN_ITERATIONS,
+                            Property.AUTHENTICATION_BUILTIN_ITERATIONS_DEFAULT,
+                            1, Integer.MAX_VALUE);
+                }
+            }
+
+            return new PasswordHasher( algorithm, salt, iterations );
+        }
+    }
+    
+    /**
+     * Generate an array of random bytes to use as salt when hashing
+     * credentials.
+     *
+     * @param props database properties that possibly specify the desired
+     *   length of the salt
+     * @return random bytes
+     */
+    private byte[] generateRandomSalt(Dictionary props) {
+        int saltLength = getIntProperty(
+                props,
+                Property.AUTHENTICATION_BUILTIN_SALT_LENGTH,
+                Property.AUTHENTICATION_BUILTIN_SALT_LENGTH_DEFAULT,
+                0, Integer.MAX_VALUE);
+
+        SecureRandom random = new SecureRandom();
+        byte[] salt = new byte[saltLength];
+        random.nextBytes(salt);
+
+        return salt;
+    }
+    /**
+     * Get the value of an integer property.
+     *
+     * @param props database properties
+     * @param key the key of the property
+     * @param defaultValue which value to return if the property is not set,
+     *   or if the property value is not in the valid range
+     * @param minValue lowest property value to accept
+     * @param maxValue highest property value to accept
+     * @return the value of the property
+     */
+    private int getIntProperty(
+            Dictionary props, String key,
+            int defaultValue, int minValue, int maxValue) {
+
+        String sVal = (String) PropertyUtil.getPropertyFromSet(props, key);
+
+        if (sVal != null) {
+            try {
+                int i = Integer.parseInt(sVal);
+                if (i >= minValue && i <= maxValue) {
+                    return i;
+                }
+            } catch (NumberFormatException nfe) {
+                // By convention, Derby ignores property values that cannot be
+                // parsed. Use the default value instead.
+            }
+        }
+
+        return defaultValue;
+    }
+
+
+    
 	/**
 	 * Get the descriptor for the system schema. Schema descriptors include 
      * authorization ids and schema ids.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Wed Jan  4 13:30:22 2012
@@ -1234,6 +1234,11 @@ Guide.
            </msg>
 
             <msg>
+                <name>4251G</name>
+                <text>Please set derby.authentication.builtin.algorithm to a valid message digest algorithm. The current authentication scheme is too weak to be used by NATIVE authentication.</text>
+           </msg>
+
+            <msg>
                 <name>42601</name>
                 <text>In an ALTER TABLE statement, the column '{0}' has been specified as NOT NULL and either the DEFAULT clause was not specified or was specified as DEFAULT NULL.</text>
                 <arg>columnName</arg>

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Wed Jan  4 13:30:22 2012
@@ -779,6 +779,7 @@ public interface SQLState {
 	String DBO_ONLY                                                         = "4251D";
 	String HIDDEN_COLUMN                                                         = "4251E";
 	String CANT_DROP_DBO                                                         = "4251F";
+	String WEAK_AUTHENTICATION                                               = "4251G";
 
 	String LANG_DB2_NOT_NULL_COLUMN_INVALID_DEFAULT                    = "42601";
 	String LANG_DB2_INVALID_HEXADECIMAL_CONSTANT                    = "42606";

Modified: db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java (original)
+++ db/derby/code/trunk/java/storeless/org/apache/derby/impl/storeless/EmptyDictionary.java Wed Jan  4 13:30:22 2012
@@ -20,6 +20,7 @@
  */
 package org.apache.derby.impl.storeless;
 
+import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Properties;
@@ -47,6 +48,7 @@ import org.apache.derby.iapi.sql.diction
 import org.apache.derby.iapi.sql.dictionary.DependencyDescriptor;
 import org.apache.derby.iapi.sql.dictionary.FileInfoDescriptor;
 import org.apache.derby.iapi.sql.dictionary.GenericDescriptorList;
+import org.apache.derby.iapi.sql.dictionary.PasswordHasher;
 import org.apache.derby.iapi.sql.dictionary.PermissionsDescriptor;
 import org.apache.derby.iapi.sql.dictionary.RoutinePermsDescriptor;
 import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
@@ -162,6 +164,13 @@ public class EmptyDictionary implements 
 		return false;
 	}
 
+    public  PasswordHasher  makePasswordHasher( Dictionary props )
+        throws StandardException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+
 	public SchemaDescriptor getSystemSchemaDescriptor()
 			throws StandardException {
 		// TODO Auto-generated method stub

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthProcs.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthProcs.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthProcs.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/NativeAuthProcs.java Wed Jan  4 13:30:22 2012
@@ -54,6 +54,10 @@ public class NativeAuthProcs extends Gen
     private static  final   String      NO_EXECUTE_PERMISSION = "42504";
     private static  final   String      DUPLICATE_USER = "X0Y68";
     private static  final   String      CANT_DROP_DBO = "4251F";
+    private static  final   String      WEAK_AUTHENTICATION = "4251G";
+
+    private static  final   String      HASHING_FORMAT_10_9 = "3b62";
+    private static  final   int           HEX_CHARS_PER_BYTE = 2;
 
     ///////////////////////////////////////////////////////////////////////////////////
     //
@@ -179,6 +183,14 @@ public class NativeAuthProcs extends Gen
         Connection  dboConnection = openUserConnection( TEST_DBO );
         Connection  janetConnection = openUserConnection( JANET );
 
+        // set a message digest algorithm in case it was unset by a previous test.
+        // a message digest algorithm must be set in order to use NATIVE authentication
+        String  defaultDigestAlgorithm = getDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm" );
+        if ( defaultDigestAlgorithm == null )
+        {
+            setDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm", "SHA-1" );
+        }
+
         if ( !dboExists( dboConnection ) )
         {
             goodStatement( dboConnection, "call syscs_util.syscs_create_user( 'TEST_DBO', 'test_dbopassword' )" );
@@ -189,6 +201,14 @@ public class NativeAuthProcs extends Gen
         resetPasswordTests( dboConnection, janetConnection );
         modifyPasswordTests( dboConnection, janetConnection );
         if ( authorizationIsOn() ) { grantRevokeTests( dboConnection, janetConnection ); }
+
+        passwordHashingTests( dboConnection );
+
+        // reset the message digest algorithm in order to not disrupt the existing tests
+        if ( defaultDigestAlgorithm == null )
+        {
+            setDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm", null );
+        }
     }
     private boolean dboExists( Connection conn )
         throws Exception
@@ -449,4 +469,118 @@ public class NativeAuthProcs extends Gen
             ( janetConnection, NO_EXECUTE_PERMISSION, "call syscs_util.syscs_drop_user( 'JOE' )" );
     }
     
+    //
+    // Create/Drop User
+    //
+    
+    private void    passwordHashingTests
+        ( Connection dboConnection )
+        throws Exception
+    {
+        String  defaultDigestAlgorithm = getDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm" );
+
+        goodStatement( dboConnection, "call syscs_util.syscs_create_user( 'pht', 'phtpassword' )" );
+
+        vetHashingScheme( dboConnection, "pht", HASHING_FORMAT_10_9, 16, 1000, defaultDigestAlgorithm );
+
+        int saltLength = 5;
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.saltLength", Integer.toString( saltLength ) );
+        goodStatement( dboConnection, "call syscs_util.syscs_reset_password( 'pht', 'newsaltlength' )" );
+        vetHashingScheme( dboConnection, "pht", HASHING_FORMAT_10_9, saltLength, 1000, defaultDigestAlgorithm );
+
+        int iterations = 10;
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.iterations", Integer.toString( iterations ) );
+        goodStatement( dboConnection, "call syscs_util.syscs_reset_password( 'pht', 'newiterations' )" );
+        vetHashingScheme( dboConnection, "pht", HASHING_FORMAT_10_9, saltLength, iterations, defaultDigestAlgorithm );
+
+        String digestAlgorithm = "SHA-1";
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm", digestAlgorithm );
+        goodStatement( dboConnection, "call syscs_util.syscs_reset_password( 'pht', 'newiterations' )" );
+        vetHashingScheme( dboConnection, "pht", HASHING_FORMAT_10_9, saltLength, iterations, digestAlgorithm );
+
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm", null );
+        expectExecutionError( dboConnection, WEAK_AUTHENTICATION, "call syscs_util.syscs_reset_password( 'pht', 'badalgorithm' )" );
+
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.saltLength", null );
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.iterations", null );
+        setDatabaseProperty( dboConnection, "derby.authentication.builtin.algorithm", defaultDigestAlgorithm );
+        goodStatement( dboConnection, "call syscs_util.syscs_drop_user( 'pht' )" );
+    }
+    private void  setDatabaseProperty( Connection conn, String key, String value )
+        throws Exception
+    {
+        if ( value == null ) { value = "cast ( null as varchar( 32672 ) )"; }
+        else { value = "'" + value + "'"; }
+        String  command = "call syscs_util.syscs_set_database_property( '" + key + "', " + value + " )";
+
+        goodStatement( conn, command );
+    }
+    private String  getDatabaseProperty( Connection conn, String key )
+        throws Exception
+    {
+        PreparedStatement   ps = chattyPrepare( conn, "values( syscs_util.syscs_get_database_property( '" + key + "' ) )" );
+        ResultSet   rs = ps.executeQuery();
+
+        try {
+            rs.next();
+            return rs.getString( 1 );
+        }
+        finally
+        {
+            rs.close();
+            ps.close();
+        }
+    }
+    private void    vetHashingScheme
+        (
+         Connection conn,
+         String userName,
+         String expectedHashingFormat,
+         int expectedSaltLength,
+         int expectedIterations,
+         String expectedDigestAlgorithm
+         )
+        throws Exception
+    {
+        String  hashingScheme = getHashingScheme( conn,  userName );
+        int     firstColonPosition = hashingScheme.indexOf( ":" );
+        int     secondColonPosition = hashingScheme.indexOf( ":", firstColonPosition + 1 );
+        int     thirdColonPosition = hashingScheme.indexOf( ":", secondColonPosition + 1 );
+
+        String  actualHashingFormat = hashingScheme.substring( 0, firstColonPosition );
+        String  salt = hashingScheme.substring( firstColonPosition + 1, secondColonPosition );
+        String  iterationString = hashingScheme.substring( secondColonPosition + 1, thirdColonPosition );
+        String  actualDigestAlgorithm = hashingScheme.substring( thirdColonPosition + 1 );
+
+        int     actualSaltLength = salt.length();
+        int     actualIterations = Integer.parseInt( iterationString );
+
+        assertEquals( expectedHashingFormat, actualHashingFormat );
+        assertEquals( expectedSaltLength * HEX_CHARS_PER_BYTE, actualSaltLength );
+        assertEquals( expectedIterations, actualIterations );
+        assertEquals( expectedDigestAlgorithm, actualDigestAlgorithm );
+    }
+    private String  getHashingScheme( Connection conn, String userName )
+        throws Exception
+    {
+        PreparedStatement   ps = conn.prepareStatement( "select userName, hashingScheme from sys.sysusers" );
+        ResultSet   rs = ps.executeQuery();
+
+        try {
+            while ( rs.next() )
+            {
+                if ( userName.equals( rs.getString( 1 )  ) ) { return rs.getString( 2 ); }
+            }
+        } finally
+        {
+            rs.close();
+            ps.close();
+        }
+
+        fail( "Could not find credentials for " + userName );
+
+        // never get here
+        return null;
+    }
+    
 }

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/upgradeTests/Changes10_9.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/upgradeTests/Changes10_9.java?rev=1227150&r1=1227149&r2=1227150&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/upgradeTests/Changes10_9.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/upgradeTests/Changes10_9.java Wed Jan  4 13:30:22 2012
@@ -204,6 +204,13 @@ public class Changes10_9 extends Upgrade
     }
     private void    vetNativeProcs( Statement s, boolean shouldExist ) throws Exception
     {
+        // make sure that an authentication algorithm has been set
+        String  defaultDigestAlgorithm = getDatabaseProperty( s, "derby.authentication.builtin.algorithm" );
+        if ( defaultDigestAlgorithm == null )
+        {
+            setDatabaseProperty( s, "derby.authentication.builtin.algorithm", "SHA-1" );
+        }
+
         try {
             s.execute( "call syscs_util.syscs_create_user( 'fred', 'fredpassword' )" );
             
@@ -236,7 +243,35 @@ public class Changes10_9 extends Upgrade
             }
             assertSQLState( "42Y03", se );
         }
-        
+
+        // restore the authentication algorithm if we changed it
+        if ( defaultDigestAlgorithm == null )
+        {
+            setDatabaseProperty( s, "derby.authentication.builtin.algorithm", null );
+        }
+    }
+    private void  setDatabaseProperty( Statement s, String key, String value )
+        throws Exception
+    {
+        if ( value == null ) { value = "cast ( null as varchar( 32672 ) )"; }
+        else { value = "'" + value + "'"; }
+        String  command = "call syscs_util.syscs_set_database_property( '" + key + "', " + value + " )";
+
+        s.execute( command );
+    }
+    private String  getDatabaseProperty( Statement s, String key )
+        throws Exception
+    {
+        ResultSet   rs = s.executeQuery( "values( syscs_util.syscs_get_database_property( '" + key + "' ) )" );
+
+        try {
+            rs.next();
+            return rs.getString( 1 );
+        }
+        finally
+        {
+            rs.close();
+        }
     }
     
     /**



Mime
View raw message