| /* |
| |
| 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.io.ArrayUtil; |
| import org.apache.derby.shared.common.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 = ArrayUtil.copy( 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; |
| } |
| } |
| } |