org.jasypt.digest.StandardStringDigester Maven / Gradle / Ivy
/*
* =============================================================================
*
* Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org)
*
* Licensed 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.jasypt.digest;
import java.security.Provider;
import org.jasypt.commons.CommonUtils;
import org.jasypt.contrib.org.apache.commons.codec_1_3.binary.Base64;
import org.jasypt.digest.config.DigesterConfig;
import org.jasypt.digest.config.StringDigesterConfig;
import org.jasypt.exceptions.AlreadyInitializedException;
import org.jasypt.exceptions.EncryptionInitializationException;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.normalization.Normalizer;
import org.jasypt.salt.SaltGenerator;
/**
*
* Standard implementation of the {@link StringDigester} interface.
* This class lets the user specify the algorithm (and provider) to be used for
* creating digests, the size of the salt to be applied,
* the number of times the hash function will be applied (iterations) and
* the salt generator to be used.
*
*
* This class avoids byte-conversion problems related to the fact of
* different platforms having different default charsets, and returns
* digests in the form of BASE64-encoded or HEXADECIMAL ASCII Strings.
*
*
* This class is thread-safe.
*
*
*
Configuration
*
*
* The algorithm, provider, salt size, iterations and salt generator can take values
* in any of these ways:
*
* - Using its default values.
* - Setting a {@link org.jasypt.digest.config.DigesterConfig}
* object which provides new
* configuration values.
* - Calling the corresponding setX(...) methods.
*
* And the actual values to be used for initialization will be established
* by applying the following priorities:
*
* - First, the default values are considered.
* - Then, if a {@link org.jasypt.digest.config.DigesterConfig}
* object has been set with
* setConfig, the non-null values returned by its
* getX methods override the default values.
* - Finally, if the corresponding setX method has been called
* on the digester itself for any of the configuration parameters, the
* values set by these calls override all of the above.
*
*
*
*
*
Initialization
*
*
* Before it is ready to create digests, an object of this class has to be
* initialized. Initialization happens:
*
* - When initialize is called.
* - When digest or matches are called for the
* first time, if initialize has not been called before.
*
* Once a digester has been initialized, trying to
* change its configuration
* will result in an AlreadyInitializedException being thrown.
*
*
*
*
Usage
*
*
* A digester may be used in two different ways:
*
* - For creating digests, by calling the digest method.
* - For matching digests, this is, checking whether a digest
* corresponds adequately to a digest (as in password checking) or not, by
* calling the matches method.
*
* The steps taken for creating digests are:
*
* - The String message is converted to a byte array.
* - A salt of the specified size is generated (see
* {@link org.jasypt.salt.SaltGenerator}).
* - The salt bytes are added to the message.
* - The hash function is applied to the salt and message altogether,
* and then to the
* results of the function itself, as many times as specified
* (iterations).
* - If specified by the salt generator (see
* {@link org.jasypt.salt.SaltGenerator#includePlainSaltInEncryptionResults()}),
* the undigested salt and the final result of the hash
* function are concatenated and returned as a result.
* - The result of the concatenation is encoded in BASE64 or HEXADECIMAL
* and returned as an ASCII String.
*
* Put schematically in bytes:
*
* -
* DIGEST = |S|..(ssb)..|S|X|X|X|...|X|
*
* - S: salt bytes (plain, not digested). (OPTIONAL).
* - ssb: salt size in bytes.
* - X: bytes resulting from hashing (see below).
*
*
* -
* |X|X|X|...|X| =
* H(H(H(..(it)..H(Z|Z|Z|...|Z|))))
*
* - H: Hash function (algorithm).
* - it: Number of iterations.
* - Z: Input for hashing (see below).
*
*
* -
* |Z|Z|Z|...|Z| =
* |S|..(ssb)..|S|M|M|M...|M|
*
* - S: salt bytes (plain, not digested).
* - ssb: salt size in bytes.
* - M: message bytes.
*
*
*
* If a random salt generator is used, two digests created for the same
* message will always be different
* (except in the case of random salt coincidence).
* Because of this, in this case the result of the digest method
* will contain both the undigested salt and the digest of the
* (salt + message), so that another digest operation can be performed
* with the same salt on a different message to check if both messages
* match (all of which will be managed automatically by the
* matches method).
*
*
* To learn more about the mechanisms involved in digest creation, read
* PKCS #5: Password-Based Cryptography Standard.
*
*
* @since 1.0
*
* @author Daniel Fernández
*
*/
public final class StandardStringDigester implements StringDigester {
/**
*
* Charset to be used to obtain "digestable" byte arrays from input Strings.
* Set to UTF-8.
*
*
* This charset has to be fixed to some value so that we avoid problems
* with different platforms having different "default" charsets.
*
*
* It is set to UTF-8 because it covers the whole spectrum of characters
* representable in Java (which internally uses UTF-16), and avoids the
* size penalty of UTF-16 (which will always use two bytes for representing
* each character, even if it is an ASCII one).
*
*
* Setting this value to UTF-8 does not mean that Strings that originally
* come for, for example, an ISO-8859-1 input, will not be correcly
* digested. It simply provides a way of "fixing" the way a String will
* be converted into bytes for digesting.
*
*/
public static final String MESSAGE_CHARSET = "UTF-8";
/**
*
* Charset to be used for encoding the resulting digests.
* Set to US-ASCII.
*
*
* The result of digesting some bytes can be any other bytes, and so
* the result of digesting, for example, some LATIN-1 valid String bytes,
* can be bytes that may not conform a "valid" LATIN-1 String.
*
*
* Because of this, digests are always encoded in BASE64 or
* HEXADECIMAL after
* being created, and this ensures that the
* digests will make perfectly representable, safe ASCII Strings. Because
* of this, the charset used to convert the digest bytes to the returned
* String is set to US-ASCII.
*
*/
public static final String DIGEST_CHARSET = "US-ASCII";
/**
*
* Whether the Unicode normalization step should be ignored because of
* legacy-compatibility issues. Defaults to FALSE (the normalization
* step WILL be performed).
*
*/
public static final boolean DEFAULT_UNICODE_NORMALIZATION_IGNORED = false;
/**
*
* Default type of String output. Set to BASE64.
*
*/
public static final String DEFAULT_STRING_OUTPUT_TYPE =
CommonUtils.STRING_OUTPUT_TYPE_BASE64;
// The StandardByteDigester that will be internally used.
private final StandardByteDigester byteDigester;
// If the config object set is a StringDigesterConfig, it must be referenced
private StringDigesterConfig stringDigesterConfig = null;
// This variable holds whether the unicode normalization step should
// be ignored or not (default = DO NOT ignore).
private boolean unicodeNormalizationIgnored =
DEFAULT_UNICODE_NORMALIZATION_IGNORED;
// This variable holds the type of String output which will be done,
// and also a boolean variable for faster comparison
private String stringOutputType = DEFAULT_STRING_OUTPUT_TYPE;
private boolean stringOutputTypeBase64 = true;
// Prefix and suffix to be added to encryption results (if any)
private String prefix = null;
private String suffix = null;
/*
* Set of booleans which indicate whether the config or default values
* have to be overriden because of the setX methods having been
* called.
*/
private boolean unicodeNormalizationIgnoredSet = false;
private boolean stringOutputTypeSet = false;
private boolean prefixSet = false;
private boolean suffixSet = false;
// BASE64 encoder which will make sure the returned digests are
// valid US-ASCII strings (if the user chooses BASE64 output).
// The Bsae64 encoder is THREAD-SAFE
private final Base64 base64;
/**
* Creates a new instance of StandardStringDigester.
*/
public StandardStringDigester() {
super();
this.byteDigester = new StandardByteDigester();
this.base64 = new Base64();
}
/*
* Creates a new instance of StandardStringDigester using
* the specified byte digester (constructor used for cloning)
*/
private StandardStringDigester(final StandardByteDigester standardByteDigester) {
super();
this.byteDigester = standardByteDigester;
this.base64 = new Base64();
}
/**
*
* Sets a {@link org.jasypt.digest.config.DigesterConfig}
* or {@link StringDigesterConfig} object
* for the digester. If this config
* object is set, it will be asked values for:
*
*
*
* - Algorithm
* - Security Provider (or provider name)
* - Salt size
* - Hashing iterations
* - Salt generator
* - Use of Unicode normalization mechanisms
* (only StringDigesterConfig)
* - Output type (base64, hexadecimal)
* (only StringDigesterConfig)
*
*
*
* The non-null values it returns will override the default ones,
* and will be overriden by any values specified with a setX
* method.
*
*
* @param config the DigesterConfig object to be used as the
* source for configuration parameters.
*/
public synchronized void setConfig(final DigesterConfig config) {
this.byteDigester.setConfig(config);
if ((config != null) && (config instanceof StringDigesterConfig)) {
this.stringDigesterConfig = (StringDigesterConfig) config;
}
}
/**
*
* Sets the algorithm to be used for digesting, like MD5
* or SHA-1.
*
*
* This algorithm has to be supported by your security infrastructure, and
* it should be allowed as an algorithm for creating
* java.security.MessageDigest instances.
*
*
* If you are specifying a security provider with {@link #setProvider(Provider)} or
* {@link #setProviderName(String)}, this algorithm should be
* supported by your specified provider.
*
*
* If you are not specifying a provider, you will be able to use those
* algorithms provided by the default security provider of your JVM vendor.
* For valid names in the Sun JVM, see Java
* Cryptography Architecture API Specification &
* Reference.
*
*
* @param algorithm the name of the algorithm to be used.
*/
public void setAlgorithm(final String algorithm) {
this.byteDigester.setAlgorithm(algorithm);
}
/**
*
* Sets the size of the salt to be used to compute the digest.
* This mechanism is explained in
* PKCS #5: Password-Based Cryptography Standard.
*
*
*
* If salt size is set to zero, then no salt will be used.
*
*
* @param saltSizeBytes the size of the salt to be used, in bytes.
*/
public void setSaltSizeBytes(final int saltSizeBytes) {
this.byteDigester.setSaltSizeBytes(saltSizeBytes);
}
/**
*
* Set the number of times the hash function will be applied recursively.
*
* The hash function will be applied to its own results as many times as
* specified: h(h(...h(x)...))
*
*
* This mechanism is explained in
* PKCS #5: Password-Based Cryptography Standard.
*
*
* @param iterations the number of iterations.
*/
public void setIterations(final int iterations) {
this.byteDigester.setIterations(iterations);
}
/**
*
* Sets the salt generator to be used. If no salt generator is specified,
* an instance of {@link org.jasypt.salt.RandomSaltGenerator} will be used.
*
*
* @param saltGenerator the salt generator to be used.
*/
public void setSaltGenerator(final SaltGenerator saltGenerator) {
this.byteDigester.setSaltGenerator(saltGenerator);
}
/**
*
* Sets the name of the security provider to be asked for the
* digest algorithm. This security provider has to be registered beforehand
* at the JVM security framework.
*
*
* The provider can also be set with the {@link #setProvider(Provider)}
* method, in which case it will not be necessary neither registering
* the provider beforehand,
* nor calling this {@link #setProviderName(String)} method to specify
* a provider name.
*
*
* Note that a call to {@link #setProvider(Provider)} overrides any value
* set by this method.
*
*
* If no provider name / provider is explicitly set, the default JVM
* provider will be used.
*
*
* @since 1.3
*
* @param providerName the name of the security provider to be asked
* for the digest algorithm.
*/
public void setProviderName(final String providerName) {
this.byteDigester.setProviderName(providerName);
}
/**
*
* Sets the security provider to be asked for the digest algorithm.
* The provider does not have to be registered at the security
* infrastructure beforehand, and its being used here will not result in
* its being registered.
*
*
* If this method is called, calling {@link #setProviderName(String)}
* becomes unnecessary.
*
*
* If no provider name / provider is explicitly set, the default JVM
* provider will be used.
*
*
* @since 1.3
*
* @param provider the provider to be asked for the chosen algorithm
*/
public void setProvider(final Provider provider) {
this.byteDigester.setProvider(provider);
}
/**
*
* Whether the salt bytes are to be appended after the
* message ones before performing the digest operation on the whole. The
* default behaviour is to insert those bytes before the message bytes, but
* setting this configuration item to true allows compatibility
* with some external systems and specifications (e.g. LDAP {SSHA}).
*
*
* If this parameter is not explicitly set, the default behaviour
* (insertion of salt before message) will be applied.
*
*
* @since 1.7
*
* @param invertPositionOfSaltInMessageBeforeDigesting
* whether salt will be appended after the message before applying
* the digest operation on the whole, instead of inserted before it
* (which is the default).
*/
public synchronized void setInvertPositionOfSaltInMessageBeforeDigesting(
final boolean invertPositionOfSaltInMessageBeforeDigesting) {
this.byteDigester.setInvertPositionOfSaltInMessageBeforeDigesting(invertPositionOfSaltInMessageBeforeDigesting);
}
/**
*
* Whether the plain (not hashed) salt bytes are to
* be appended after the digest operation result bytes. The default behaviour is
* to insert them before the digest result, but setting this configuration
* item to true allows compatibility with some external systems
* and specifications (e.g. LDAP {SSHA}).
*
*
* If this parameter is not explicitly set, the default behaviour
* (insertion of plain salt before digest result) will be applied.
*
*
* @since 1.7
*
* @param invertPositionOfPlainSaltInEncryptionResults
* whether plain salt will be appended after the digest operation
* result instead of inserted before it (which is the
* default).
*/
public synchronized void setInvertPositionOfPlainSaltInEncryptionResults(
final boolean invertPositionOfPlainSaltInEncryptionResults) {
this.byteDigester.setInvertPositionOfPlainSaltInEncryptionResults(invertPositionOfPlainSaltInEncryptionResults);
}
/**
*
* Whether digest matching operations will allow matching
* digests with a salt size different to the one configured in the "saltSizeBytes"
* property. This is possible because digest algorithms will produce a fixed-size
* result, so the remaining bytes from the hashed input will be considered salt.
*
*
* This will allow the digester to match digests produced in environments which do not
* establish a fixed salt size as standard (for example, SSHA password encryption
* in LDAP systems).
*
*
* The value of this property will not affect the creation of digests,
* which will always have a salt of the size established by the "saltSizeBytes"
* property. It will only affect digest matching.
*
*
* Setting this property to true is not compatible with {@link SaltGenerator}
* implementations which return false for their
* {@link SaltGenerator#includePlainSaltInEncryptionResults()} property.
*
*
* Also, be aware that some algorithms or algorithm providers might not support
* knowing the size of the digests beforehand, which is also incompatible with
* a lenient behaviour.
*
*
* If this parameter is not explicitly set, the default behaviour
* (NOT lenient) will be applied.
*
*
* @since 1.7
*
* @param useLenientSaltSizeCheck whether the digester will allow matching of
* digests with different salt sizes than established or not (default
* is false).
*/
public synchronized void setUseLenientSaltSizeCheck(final boolean useLenientSaltSizeCheck) {
this.byteDigester.setUseLenientSaltSizeCheck(useLenientSaltSizeCheck);
}
/**
*
* Sets whether the unicode text normalization step should be ignored.
*
*
* The Java Virtual Machine internally handles all Strings as UNICODE. When
* digesting or matching digests in jasypt, these Strings are first
* normalized to
* its NFC form so that digest matching is not affected by the specific
* form in which the messages where input.
*
*
* It is normally safe (and recommended) to leave this parameter set to
* its default FALSE value (and thus DO perform normalization
* operations). But in some specific cases in which issues with legacy
* software could arise, it might be useful to set this to TRUE.
*
*
* For more information on unicode text normalization, see this issue of
* Core Java Technologies Tech Tips.
*
*
* @since 1.3
*
* @param unicodeNormalizationIgnored whether the unicode text
* normalization step should be ignored or not.
*/
public synchronized void setUnicodeNormalizationIgnored(final boolean unicodeNormalizationIgnored) {
if (isInitialized()) {
throw new AlreadyInitializedException();
}
this.unicodeNormalizationIgnored = unicodeNormalizationIgnored;
this.unicodeNormalizationIgnoredSet = true;
}
/**
*
* Sets the the form in which String output
* will be encoded. Available encoding types are:
*
*
* - base64 (default)
* - hexadecimal
*
*
* If not set, null will be returned.
*
*
* @since 1.3
*
* @param stringOutputType the string output type.
*/
public synchronized void setStringOutputType(final String stringOutputType) {
CommonUtils.validateNotEmpty(stringOutputType, "String output type cannot be set empty");
if (isInitialized()) {
throw new AlreadyInitializedException();
}
this.stringOutputType =
CommonUtils.
getStandardStringOutputType(stringOutputType);
this.stringOutputTypeSet = true;
}
/**
*
* Sets the prefix to be added at the beginning of encryption results, and also to
* be expected at the beginning of plain messages provided for matching operations
* (raising an {@link EncryptionOperationNotPossibleException} if not).
*
*
* By default, no prefix will be added to encryption results.
*
*
* @since 1.7
*
* @param prefix the prefix to be set
*/
public synchronized void setPrefix(final String prefix) {
if (isInitialized()) {
throw new AlreadyInitializedException();
}
this.prefix = prefix;
this.prefixSet = true;
}
/**
*
* Sets the suffix to be added at the end of encryption results, and also to
* be expected at the end of plain messages provided for matching operations
* (raising an {@link EncryptionOperationNotPossibleException} if not).
*
*
* By default, no suffix will be added to encryption results.
*
*
* @since 1.7
*
* @param suffix the suffix to be set
*/
public synchronized void setSuffix(final String suffix) {
if (isInitialized()) {
throw new AlreadyInitializedException();
}
this.suffix = suffix;
this.suffixSet = true;
}
/*
* Clone this digester.
*/
StandardStringDigester cloneDigester() {
// Check initialization
if (!isInitialized()) {
initialize();
}
final StandardStringDigester cloned =
new StandardStringDigester(this.byteDigester.cloneDigester());
cloned.setPrefix(this.prefix);
cloned.setSuffix(this.suffix);
if (CommonUtils.isNotEmpty(this.stringOutputType)) {
cloned.setStringOutputType(this.stringOutputType);
}
cloned.setUnicodeNormalizationIgnored(this.unicodeNormalizationIgnored);
return cloned;
}
/**
*
* Returns true if the digester has already been initialized, false if
* not.
* Initialization happens:
*
*
* - When initialize is called.
* - When digest or matches are called for the
* first time, if initialize has not been called before.
*
*
* Once a digester has been initialized, trying to
* change its configuration will result in an
* AlreadyInitializedException
* being thrown.
*
*
* @return true if the digester has already been initialized, false if
* not.
*/
public boolean isInitialized() {
return this.byteDigester.isInitialized();
}
/**
*
* Initialize the digester.
*
*
* This operation will consist in determining the actual configuration
* values to be used, and then initializing the digester with them.
*
* These values are decided by applying the following priorities:
*
*
* - First, the default values are considered.
* - Then, if a
* {@link org.jasypt.digest.config.DigesterConfig} object
* has been set with
* setConfig, the non-null values returned by its
* getX methods override the default values.
* - Finally, if the corresponding setX method has been called
* on the digester itself for any of the configuration parameters, the
* values set by these calls override all of the above.
*
*
* Once a digester has been initialized, trying to
* change its configuration will result in an
* AlreadyInitializedException
* being thrown.
*
*
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, if the digest algorithm chosen
* cannot be used).
*
*/
public synchronized void initialize() {
// Double-check to avoid synchronization issues
if (!this.isInitialized()) {
/*
* If a StringDigesterConfig object has been set, we need to
* consider the values it returns (if, for each value, the
* corresponding "setX" method has not been called).
*/
if (this.stringDigesterConfig != null) {
final Boolean configUnicodeNormalizationIgnored =
this.stringDigesterConfig.isUnicodeNormalizationIgnored();
final String configStringOutputType =
this.stringDigesterConfig.getStringOutputType();
final String configPrefix =
this.stringDigesterConfig.getPrefix();
final String configSuffix =
this.stringDigesterConfig.getSuffix();
this.unicodeNormalizationIgnored =
((this.unicodeNormalizationIgnoredSet) || (configUnicodeNormalizationIgnored == null))?
this.unicodeNormalizationIgnored : configUnicodeNormalizationIgnored.booleanValue();
this.stringOutputType =
((this.stringOutputTypeSet) || (configStringOutputType == null))?
this.stringOutputType : configStringOutputType;
this.prefix =
((this.prefixSet) || (configPrefix == null))?
this.prefix : configPrefix;
this.suffix =
((this.suffixSet) || (configSuffix == null))?
this.suffix : configSuffix;
}
this.stringOutputTypeBase64 =
(CommonUtils.STRING_OUTPUT_TYPE_BASE64.
equalsIgnoreCase(this.stringOutputType));
this.byteDigester.initialize();
}
}
/**
*
* Performs a digest operation on a String message.
*
*
* The steps taken for creating the digest are:
*
* - The String message is converted to a byte array.
* - A salt of the specified size is generated (see
* {@link org.jasypt.salt.SaltGenerator}).
* - The salt bytes are added to the message.
* - The hash function is applied to the salt and message altogether,
* and then to the
* results of the function itself, as many times as specified
* (iterations).
* - If specified by the salt generator (see
* {@link org.jasypt.salt.SaltGenerator#includePlainSaltInEncryptionResults()}),
* the undigested salt and the final result of the hash
* function are concatenated and returned as a result.
* - The result of the concatenation is encoded in BASE64 (default)
* or HEXADECIMAL
* and returned as an ASCII String.
*
* Put schematically in bytes:
*
* -
* DIGEST = |S|..(ssb)..|S|X|X|X|...|X|
*
* - S: salt bytes (plain, not digested). (OPTIONAL).
* - ssb: salt size in bytes.
* - X: bytes resulting from hashing (see below).
*
*
* -
* |X|X|X|...|X| =
* H(H(H(..(it)..H(Z|Z|Z|...|Z|))))
*
* - H: Hash function (algorithm).
* - it: Number of iterations.
* - Z: Input for hashing (see below).
*
*
* -
* |Z|Z|Z|...|Z| =
* |S|..(ssb)..|S|M|M|M...|M|
*
* - S: salt bytes (plain, not digested).
* - ssb: salt size in bytes.
* - M: message bytes.
*
*
*
*
*
* If a random salt generator is used, two digests created for the same
* message will always be different
* (except in the case of random salt coincidence).
* Because of this, in this case the result of the digest method
* will contain both the undigested salt and the digest of the
* (salt + message), so that another digest operation can be performed
* with the same salt on a different message to check if both messages
* match (all of which will be managed automatically by the
* matches method).
*
*
* @param message the String to be digested
* @return the digest result
* @throws EncryptionOperationNotPossibleException if the digest operation
* fails, ommitting any further information about the cause for
* security reasons.
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, if the digest algorithm chosen
* cannot be used).
*/
public String digest(final String message) {
if (message == null) {
return null;
}
// Check initialization
if (!isInitialized()) {
initialize();
}
try {
// Normalize Unicode message to NFC form
String normalizedMessage = null;
if (! this.unicodeNormalizationIgnored) {
normalizedMessage = Normalizer.normalizeToNfc(message);
} else {
normalizedMessage = message;
}
// The input String is converted into bytes using MESSAGE_CHARSET
// as a fixed charset to avoid problems with different platforms
// having different default charsets (see MESSAGE_CHARSET doc).
final byte[] messageBytes = normalizedMessage.getBytes(MESSAGE_CHARSET);
// The StandardByteDigester does its job.
byte[] digest = this.byteDigester.digest(messageBytes);
// We build the result variable
final StringBuffer result = new StringBuffer();
if (this.prefix != null) {
// Prefix is added
result.append(this.prefix);
}
// We encode the result in BASE64 or HEXADECIMAL so that we obtain
// the safest result String possible.
if (this.stringOutputTypeBase64) {
digest = this.base64.encode(digest);
result.append(new String(digest, DIGEST_CHARSET));
} else {
result.append(CommonUtils.toHexadecimal(digest));
}
if (this.suffix != null) {
// Suffix is added
result.append(this.suffix);
}
return result.toString();
} catch (EncryptionInitializationException e) {
throw e;
} catch (EncryptionOperationNotPossibleException e) {
throw e;
} catch (Exception e) {
// If digest fails, it is more secure not to return any information
// about the cause in nested exceptions. Simply fail.
throw new EncryptionOperationNotPossibleException();
}
}
/**
*
* Checks a message against a given digest.
*
*
* This method tells whether a message corresponds to a specific digest
* or not by getting the salt with which the digest was created and
* applying it to a digest operation performed on the message. If
* new and existing digest match, the message is said to match the digest.
*
*
* This method will be used, for instance, for password checking in
* authentication processes.
*
*
* A null message will only match a null digest.
*
*
* @param message the message to be compared to the digest.
* @param digest the digest.
* @return true if the specified message matches the digest, false
* if not.
* @throws EncryptionOperationNotPossibleException if the digest matching
* operation fails, ommitting any further information about the
* cause for security reasons.
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, if the digest algorithm chosen
* cannot be used).
*/
public boolean matches(final String message, final String digest) {
String processedDigest = digest;
if (processedDigest != null) {
if (this.prefix != null) {
if (!processedDigest.startsWith(this.prefix)) {
throw new EncryptionOperationNotPossibleException(
"Digest does not start with required prefix \"" + this.prefix + "\"");
}
processedDigest = processedDigest.substring(this.prefix.length());
}
if (this.suffix != null) {
if (!processedDigest.endsWith(this.suffix)) {
throw new EncryptionOperationNotPossibleException(
"Digest does not end with required suffix \"" + this.suffix + "\"");
}
processedDigest = processedDigest.substring(0, processedDigest.length() - this.suffix.length());
}
}
if (message == null) {
return (processedDigest == null);
} else if (processedDigest == null) {
return false;
}
// Check initialization
if (!isInitialized()) {
initialize();
}
try {
// Normalize Unicode message to NFC form
String normalizedMessage = null;
if (! this.unicodeNormalizationIgnored) {
normalizedMessage = Normalizer.normalizeToNfc(message);
} else {
normalizedMessage = message;
}
// We get a valid byte array from the message, in the
// fixed MESSAGE_CHARSET that the digest operations use.
final byte[] messageBytes = normalizedMessage.getBytes(MESSAGE_CHARSET);
// The BASE64 or HEXADECIMAL encoding is reversed and the digest
// is converted into a byte array.
byte[] digestBytes = null;
if (this.stringOutputTypeBase64) {
// The digest must be a US-ASCII String BASE64-encoded
digestBytes = processedDigest.getBytes(DIGEST_CHARSET);
digestBytes = this.base64.decode(digestBytes);
} else {
digestBytes = CommonUtils.fromHexadecimal(processedDigest);
}
// The StandardByteDigester is asked to match message to digest.
return this.byteDigester.matches(messageBytes, digestBytes);
} catch (EncryptionInitializationException e) {
throw e;
} catch (EncryptionOperationNotPossibleException e) {
throw e;
} catch (Exception e) {
// If digest fails, it is more secure not to return any information
// about the cause in nested exceptions. Simply fail.
throw new EncryptionOperationNotPossibleException();
}
}
}