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

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

The 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.Provider;

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



/**
 * 

* Pooled implementation of {@link ByteDigester} that in fact contains * an array of {@link StandardByteDigester} objects which are used * to attend digest and match requests in round-robin. This should * result in higher performance in multiprocessor systems. *

*

* Configuration of this class is equivalent to that of * {@link StandardByteDigester}. *

*

* This class is thread-safe. *

* * * @since 1.7 * * @author Daniel Fernández * */ public class PooledByteDigester implements ByteDigester { private final StandardByteDigester firstDigester; private DigesterConfig config = null; private int poolSize = 0; private boolean poolSizeSet = false; private StandardByteDigester[] pool; private int roundRobin = 0; /* * 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; /** * Creates a new instance of PooledStandardByteDigester. */ public PooledByteDigester() { super(); this.firstDigester = new StandardByteDigester(); } /** *

* 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) { this.firstDigester.setConfig(config); 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) { this.firstDigester.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 synchronized void setSaltSizeBytes(final int saltSizeBytes) { this.firstDigester.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 synchronized void setIterations(final int iterations) { this.firstDigester.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 synchronized void setSaltGenerator(final SaltGenerator saltGenerator) { this.firstDigester.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. *

* * @param providerName the name of the security provider to be asked * for the digest algorithm. */ public synchronized void setProviderName(final String providerName) { this.firstDigester.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. *

* * @param provider the provider to be asked for the chosen algorithm */ public synchronized void setProvider(final Provider provider) { this.firstDigester.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. *

* * @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.firstDigester.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.firstDigester.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. *

* * @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.firstDigester.setUseLenientSaltSizeCheck(useLenientSaltSizeCheck); } /** *

* Sets the size of the pool of digesters to be created. *

*

* This parameter is required. *

* * @param poolSize size of the pool */ public synchronized void setPoolSize(final int poolSize) { CommonUtils.validateIsTrue(poolSize > 0, "Pool size be > 0"); if (isInitialized()) { throw new AlreadyInitializedException(); } this.poolSize = poolSize; this.poolSizeSet = true; } /** *

* 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 (this.config != null) { final Integer configPoolSize = this.config.getPoolSize(); this.poolSize = ((this.poolSizeSet) || (configPoolSize == null))? this.poolSize : configPoolSize.intValue(); } if (this.poolSize <= 0) { throw new IllegalArgumentException("Pool size must be set and > 0"); } this.pool = new StandardByteDigester[this.poolSize]; this.pool[0] = this.firstDigester; for (int i = 1; i < this.poolSize; i++) { this.pool[i] = this.pool[i - 1].cloneDigester(); } 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) { // Check initialization if (!isInitialized()) { initialize(); } int poolPosition; synchronized(this) { poolPosition = this.roundRobin; this.roundRobin = (this.roundRobin + 1) % this.poolSize; } return this.pool[poolPosition].digest(message); } /** *

* 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) { // Check initialization if (!isInitialized()) { initialize(); } int poolPosition; synchronized(this) { poolPosition = this.roundRobin; this.roundRobin = (this.roundRobin + 1) % this.poolSize; } return this.pool[poolPosition].matches(message, digest); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy