All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jasypt.digest.StandardByteDigester Maven / Gradle / Ivy

There is a newer version: 6.1.4
Show newest version
/*
 * =============================================================================
 * 
 *   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.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;

import org.jasypt.commons.CommonUtils;
import org.jasypt.digest.config.DigesterConfig;
import org.jasypt.exceptions.AlreadyInitializedException;
import org.jasypt.exceptions.EncryptionInitializationException;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.salt.RandomSaltGenerator;
import org.jasypt.salt.SaltGenerator;

/**
 * 

* Standard implementation of the {@link ByteDigester} 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 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 setAlgorithm(...), setProvider(...), * setProviderName(...), * setSaltSizeBytes(...), setIterations(...) * or setSaltGenerator(...) methods.
  • *
* And the actual values to be used for initialization will be established * by applying the following priorities: *
    *
  1. First, the default values are considered.
  2. *
  3. 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.
  4. *
  5. 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.
  6. *
*

* *

*
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 (algorithm, provider, salt size, iterations or * salt generator) * 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: *
    *
  1. A salt of the specified size is generated (see * {@link SaltGenerator}). If salt size is zero, no salt will be * used.
  2. *
  3. The salt bytes are added to the message.
  4. *
  5. 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).
  6. *
  7. 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.
  8. *
* 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 StandardByteDigester implements ByteDigester { /** * Default digest algorithm will be MD5 */ public static final String DEFAULT_ALGORITHM = "MD5"; /** * The minimum recommended size for salt is 8 bytes */ public static final int DEFAULT_SALT_SIZE_BYTES = 8; /** * The minimum recommended iterations for hashing are 1000 */ public static final int DEFAULT_ITERATIONS = 1000; // Algorithm to be used for hashing private String algorithm = DEFAULT_ALGORITHM; // Size of salt to be applied private int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES; // Number of hash iterations to be applied private int iterations = DEFAULT_ITERATIONS; // SaltGenerator to be used. Initialization of a salt generator is costly, // and so default value will be applied only in initialize(), if it finally // becomes necessary. private SaltGenerator saltGenerator = null; // Name of the java.security.Provider which will be asked for the selected // algorithm private String providerName = null; // java.security.Provider instance which will be asked for the selected // algorithm private Provider provider = null; // Whether salt bytes will be appended after message before digesting or // inserted before it (which is the default). private boolean invertPositionOfSaltInMessageBeforeDigesting = false; // Whether plain (unhashed) salt bytes will be appended after the digest // operation result or inserted before it (which is the default). private boolean invertPositionOfPlainSaltInEncryptionResults = false; // Whether digest matching operations will allow matching digests with a // salt size different to the one configured in the "saltSizeBytes" property. private boolean useLenientSaltSizeCheck = false; /* * Config: this object can set a configuration by bringing the values in * whichever way the developer wants (it only has to implement the * DigesterConfig interface). * * Calls to setX methods OVERRIDE the values brought by this config. */ private DigesterConfig config = 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 algorithmSet = false; private boolean saltSizeBytesSet = false; private boolean iterationsSet = false; private boolean saltGeneratorSet = false; private boolean providerNameSet = false; private boolean providerSet = false; private boolean invertPositionOfSaltInMessageBeforeDigestingSet = false; private boolean invertPositionOfPlainSaltInEncryptionResultsSet = false; private boolean useLenientSaltSizeCheckSet = false; /* * Flag which indicates whether the digester has been initialized or not. * * Once initialized, no further modifications to its configuration will * be allowed. */ private boolean initialized = false; /* * If the salt size is set to a value higher than zero, this flag will * indicate that the salt mecanism has to be used. */ private boolean useSalt = true; /* * MessageDigest to be used. * * IMPORTANT: MessageDigest is not a thread-safe class, and thus any * use of this variable will have to be adequately synchronized. */ private MessageDigest md = null; /* * Length of the result digest for the specified algorithm. * This might be zero if this operation is not supported by the * algorithm provider and the implementation is not cloneable. */ private int digestLengthBytes = 0; /** * Creates a new instance of StandardByteDigester. */ public StandardByteDigester() { super(); } /** *

* Sets a {@link org.jasypt.digest.config.DigesterConfig} 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
  • *
  • Location of the salt in relation to the encrypted message * (default: before)
  • *
* *

* 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) { CommonUtils.validateNotNull(config, "Config cannot be set null"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.config = 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 synchronized void setAlgorithm(final String algorithm) { CommonUtils.validateNotEmpty(algorithm, "Algorithm cannot be empty"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.algorithm = algorithm; this.algorithmSet = true; } /** *

* 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 synchronized void setSaltSizeBytes(final int saltSizeBytes) { CommonUtils.validateIsTrue(saltSizeBytes >= 0, "Salt size in bytes must be non-negative"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.saltSizeBytes = saltSizeBytes; this.useSalt = (saltSizeBytes > 0); this.saltSizeBytesSet = true; } /** *

* 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 synchronized void setIterations(final int iterations) { CommonUtils.validateIsTrue(iterations > 0, "Number of iterations must be greater than zero"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.iterations = iterations; this.iterationsSet = true; } /** *

* Sets the salt generator to be used. If no salt generator is specified, * an instance of {@link org.jasypt.salt.RandomSaltGenerator} will be used. *

* * @since 1.2 * * @param saltGenerator the salt generator to be used. */ public synchronized void setSaltGenerator(final SaltGenerator saltGenerator) { CommonUtils.validateNotNull(saltGenerator, "Salt generator cannot be set null"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.saltGenerator = saltGenerator; this.saltGeneratorSet = true; } /** *

* 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 synchronized void setProviderName(final String providerName) { CommonUtils.validateNotNull(providerName, "Provider name cannot be set null"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.providerName = providerName; this.providerNameSet = true; } /** *

* 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 synchronized void setProvider(final Provider provider) { CommonUtils.validateNotNull(provider, "Provider cannot be set null"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.provider = provider; this.providerSet = true; } /** *

* 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) { if (isInitialized()) { throw new AlreadyInitializedException(); } this.invertPositionOfSaltInMessageBeforeDigesting = invertPositionOfSaltInMessageBeforeDigesting; this.invertPositionOfSaltInMessageBeforeDigestingSet = true; } /** *

* 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) { if (isInitialized()) { throw new AlreadyInitializedException(); } this.invertPositionOfPlainSaltInEncryptionResults = invertPositionOfPlainSaltInEncryptionResults; this.invertPositionOfPlainSaltInEncryptionResultsSet = true; } /** *

* 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) { if (isInitialized()) { throw new AlreadyInitializedException(); } this.useLenientSaltSizeCheck = useLenientSaltSizeCheck; this.useLenientSaltSizeCheckSet = true; } /* * Clone this digester. */ StandardByteDigester cloneDigester() { // Check initialization if (!isInitialized()) { initialize(); } final StandardByteDigester cloned = new StandardByteDigester(); if (CommonUtils.isNotEmpty(this.algorithm)) { cloned.setAlgorithm(this.algorithm); } cloned.setInvertPositionOfPlainSaltInEncryptionResults(this.invertPositionOfPlainSaltInEncryptionResults); cloned.setInvertPositionOfSaltInMessageBeforeDigesting(this.invertPositionOfSaltInMessageBeforeDigesting); cloned.setIterations(this.iterations); if (this.provider != null) { cloned.setProvider(this.provider); } if (this.providerName != null) { cloned.setProviderName(this.providerName); } if (this.saltGenerator != null) { cloned.setSaltGenerator(this.saltGenerator); } cloned.setSaltSizeBytes(this.saltSizeBytes); cloned.setUseLenientSaltSizeCheck(this.useLenientSaltSizeCheck); 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 (algorithm, provider, salt size, iterations * or salt generator) will * result in an AlreadyInitializedException being thrown. *

* * @return true if the digester has already been initialized, false if * not. */ public boolean isInitialized() { return this.initialized; } /** *

* 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: *

*
    *
  1. First, the default values are considered.
  2. *
  3. 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.
  4. *
  5. 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.
  6. *
*

* 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.initialized) { /* * If a DigesterConfig 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.config != null) { final String configAlgorithm = this.config.getAlgorithm(); if (configAlgorithm != null) { CommonUtils.validateNotEmpty(configAlgorithm, "Algorithm cannot be empty"); } final Integer configSaltSizeBytes = this.config.getSaltSizeBytes(); if (configSaltSizeBytes != null) { CommonUtils.validateIsTrue(configSaltSizeBytes.intValue() >= 0, "Salt size in bytes must be non-negative"); } final Integer configIterations = this.config.getIterations(); if (configIterations != null) { CommonUtils.validateIsTrue(configIterations.intValue() > 0, "Number of iterations must be greater than zero"); } final SaltGenerator configSaltGenerator = this.config.getSaltGenerator(); final String configProviderName = this.config.getProviderName(); if (configProviderName != null) { CommonUtils.validateNotEmpty(configProviderName, "Provider name cannot be empty"); } final Provider configProvider = this.config.getProvider(); final Boolean configInvertPositionOfSaltInMessageBeforeDigesting = this.config.getInvertPositionOfSaltInMessageBeforeDigesting(); final Boolean configInvertPositionOfPlainSaltInEncryptionResults = this.config.getInvertPositionOfPlainSaltInEncryptionResults(); final Boolean configUseLenientSaltSizeCheck = this.config.getUseLenientSaltSizeCheck(); this.algorithm = ((this.algorithmSet) || (configAlgorithm == null))? this.algorithm : configAlgorithm; this.saltSizeBytes = ((this.saltSizeBytesSet) || (configSaltSizeBytes == null))? this.saltSizeBytes : configSaltSizeBytes.intValue(); this.iterations = ((this.iterationsSet) || (configIterations == null))? this.iterations : configIterations.intValue(); this.saltGenerator = ((this.saltGeneratorSet) || (configSaltGenerator == null))? this.saltGenerator : configSaltGenerator; this.providerName = ((this.providerNameSet) || (configProviderName == null))? this.providerName : configProviderName; this.provider = ((this.providerSet) || (configProvider == null))? this.provider : configProvider; this.invertPositionOfSaltInMessageBeforeDigesting = ((this.invertPositionOfSaltInMessageBeforeDigestingSet) || (configInvertPositionOfSaltInMessageBeforeDigesting == null))? this.invertPositionOfSaltInMessageBeforeDigesting : configInvertPositionOfSaltInMessageBeforeDigesting.booleanValue(); this.invertPositionOfPlainSaltInEncryptionResults = ((this.invertPositionOfPlainSaltInEncryptionResultsSet) || (configInvertPositionOfPlainSaltInEncryptionResults == null))? this.invertPositionOfPlainSaltInEncryptionResults : configInvertPositionOfPlainSaltInEncryptionResults.booleanValue(); this.useLenientSaltSizeCheck = ((this.useLenientSaltSizeCheckSet) || (configUseLenientSaltSizeCheck == null))? this.useLenientSaltSizeCheck : configUseLenientSaltSizeCheck.booleanValue(); } /* * If the digester was not set a salt generator in any way, * it is time to apply its default value. */ if (this.saltGenerator == null) { this.saltGenerator = new RandomSaltGenerator(); } /* * Test compatibility of salt generator with salt size checking * behaviour */ if (this.useLenientSaltSizeCheck) { if (!this.saltGenerator.includePlainSaltInEncryptionResults()) { throw new EncryptionInitializationException( "The configured Salt Generator (" + this.saltGenerator.getClass().getName() + ") does not include plain salt " + "in encryption results, which is not compatible" + "with setting the salt size checking behaviour to \"lenient\"."); } } /* * MessageDigest is initialized the usual way, and the digester * is marked as "initialized" so that configuration cannot be * changed in the future. */ try { if (this.provider != null) { this.md = MessageDigest.getInstance( this.algorithm, this.provider); } else if (this.providerName != null) { this.md = MessageDigest.getInstance( this.algorithm, this.providerName); } else { this.md = MessageDigest.getInstance(this.algorithm); } } catch (NoSuchAlgorithmException e) { throw new EncryptionInitializationException(e); } catch (NoSuchProviderException e) { throw new EncryptionInitializationException(e); } /* * Store the digest length (algorithm-dependent) and check * the operation is supported by the provider. */ this.digestLengthBytes = this.md.getDigestLength(); if (this.digestLengthBytes <= 0) { throw new EncryptionInitializationException( "The configured algorithm (" + this.algorithm + ") or its provider do " + "not allow knowing the digest length beforehand " + "(getDigestLength() operation), which is not compatible" + "with setting the salt size checking behaviour to \"lenient\"."); } this.initialized = true; } } /** *

* Performs a digest operation on a byte array message. *

*

* The steps taken for creating the digest are: *

    *
  1. A salt of the specified size is generated (see * {@link SaltGenerator}).
  2. *
  3. The salt bytes are added to the message.
  4. *
  5. 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).
  6. *
  7. 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.
  8. *
* 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 byte array 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 byte[] digest(byte[] message) { if (message == null) { return null; } // Check initialization if (!isInitialized()) { initialize(); } // Create salt byte[] salt = null; if (this.useSalt) { salt = this.saltGenerator.generateSalt(this.saltSizeBytes); } // Create digest return digest(message, salt); } /* * This method truly performs the digest operation, assuming that a salt * has already been created (if needed) and the digester has already been * initialized. */ private byte[] digest(final byte[] message, final byte[] salt) { try { byte[] digest = null; synchronized (this.md) { this.md.reset(); if (salt != null) { if (!this.invertPositionOfSaltInMessageBeforeDigesting) { // The salt bytes are added before the message to be digested this.md.update(salt); this.md.update(message); } else { // The salt bytes are appended after the message to be digested this.md.update(message); this.md.update(salt); } } else { //No salt to be added this.md.update(message); } digest = this.md.digest(); for (int i = 0; i < (this.iterations - 1); i++) { this.md.reset(); digest = this.md.digest(digest); } } // Finally we build an array containing both the unhashed (plain) salt // and the digest of the (salt + message). This is done only // if the salt generator we are using specifies to do so. if (this.saltGenerator.includePlainSaltInEncryptionResults() && salt != null) { if (!this.invertPositionOfPlainSaltInEncryptionResults) { // Insert unhashed salt before the hashing result (default behaviour) return CommonUtils.appendArrays(salt, digest); } // Append unhashed salt after the hashing result return CommonUtils.appendArrays(digest, salt); } return digest; } 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 byte[] message, final byte[] digest) { if (message == null) { return (digest == null); } else if (digest == null) { return false; } // Check initialization if (!isInitialized()) { initialize(); } try { // If we are using a salt, extract it to use it. byte[] salt = null; if (this.useSalt) { // If we are using a salt generator which specifies the salt // to be included into the digest itself, get it from there. // If not, the salt is supposed to be fixed and thus the // salt generator can be safely asked for it again. if (this.saltGenerator.includePlainSaltInEncryptionResults()) { // Compute size figures and perform length checks int digestSaltSize = this.saltSizeBytes; if (this.digestLengthBytes > 0) { if (this.useLenientSaltSizeCheck) { if (digest.length < this.digestLengthBytes) { throw new EncryptionOperationNotPossibleException(); } digestSaltSize = digest.length - this.digestLengthBytes; } else { if (digest.length != (this.digestLengthBytes + this.saltSizeBytes)) { throw new EncryptionOperationNotPossibleException(); } } } else { // Salt size check behaviour cannot be set to lenient if (digest.length < this.saltSizeBytes) { throw new EncryptionOperationNotPossibleException(); } } if (!this.invertPositionOfPlainSaltInEncryptionResults) { salt = new byte[digestSaltSize]; System.arraycopy(digest, 0, salt, 0, digestSaltSize); } else { salt = new byte[digestSaltSize]; System.arraycopy(digest, digest.length - digestSaltSize, salt, 0, digestSaltSize); } } else { salt = this.saltGenerator.generateSalt(this.saltSizeBytes); } } // Digest the message with the extracted digest. final byte[] encryptedMessage = digest(message, salt); // If, using the same salt, digests match, then messages too. return (digestsAreEqual(encryptedMessage, digest)); } 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(); } } // Time-constant comparison of byte arrays private static boolean digestsAreEqual(byte[] a, byte[] b) { if (a == null || b == null) { return false; } final int aLen = a.length; if (b.length != aLen) { return false; } int match = 0; for (int i = 0; i < aLen; i++) { match |= a[i] ^ b[i]; } return (match == 0); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy