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

org.jasypt.digest.PooledStringDigester 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.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 StringDigester} that in fact contains * an array of {@link StandardStringDigester} 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 StandardStringDigester}. *

*

* This class is thread-safe. *

* * * @since 1.7 * * @author Daniel Fernández * */ public class PooledStringDigester implements StringDigester { private final StandardStringDigester firstDigester; private DigesterConfig config = null; private int poolSize = 0; private boolean poolSizeSet = false; private StandardStringDigester[] 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 PooledStandardStringDigester. */ public PooledStringDigester() { super(); this.firstDigester = new StandardStringDigester(); } /** *

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

* * @param unicodeNormalizationIgnored whether the unicode text * normalization step should be ignored or not. */ public synchronized void setUnicodeNormalizationIgnored(final boolean unicodeNormalizationIgnored) { this.firstDigester.setUnicodeNormalizationIgnored(unicodeNormalizationIgnored); } /** *

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

* * @param stringOutputType the string output type. */ public synchronized void setStringOutputType(final String stringOutputType) { this.firstDigester.setStringOutputType(stringOutputType); } /** *

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

* * @param prefix the prefix to be set */ public synchronized void setPrefix(final String prefix) { this.firstDigester.setPrefix(prefix); } /** *

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

* * @param suffix the suffix to be set */ public synchronized void setSuffix(final String suffix) { this.firstDigester.setSuffix(suffix); } /** *

* 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 StandardStringDigester[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 String message. *

*

* The steps taken for creating the digest are: *

    *
  1. The String message is converted to a byte array.
  2. *
  3. A salt of the specified size is generated (see * {@link org.jasypt.salt.SaltGenerator}).
  4. *
  5. The salt bytes are added to the message.
  6. *
  7. 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).
  8. *
  9. 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.
  10. *
  11. The result of the concatenation is encoded in BASE64 (default) * or HEXADECIMAL * and returned as an ASCII String.
  12. *
* 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) { // 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 String message, final String 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