Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * =============================================================================
   * 
   *   Copyright (c) 2007, 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;
 
 

Standard implementation of the 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 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. Then, if a org.jasypt.digest.config.DigesterConfig object has been set with setConfig, the non-null values returned by its getX methods override the default values.
  3. 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 (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 org.jasypt.salt.SaltGenerator). If salt size is zero, no salt will be used.
  2. The salt bytes are added to the message.
  3. 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).
  4. If specified by the salt generator (see org.jasypt.salt.SaltGenerator.includePlainSaltInEncryptionResults()), the undigested salt and the final result of the hash function are concatenated and returned as a result.
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.

Author(s):
Daniel Fernández Garrido
Since:
1.0
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 = ;
    // Size of salt to be applied
    private int saltSizeBytes = ;
    // Number of hash iterations to be applied
    private int 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;
    
    /*
     * 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;
    /*
     * 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;
    

    
    
Creates a new instance of StandardByteDigester.
    public StandardByteDigester() {
        super();
    }
    
    
    

Sets a 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

The non-null values it returns will override the default ones, and will be overriden by any values specified with a setX method.

Parameters:
config the DigesterConfig object to be used as the source for configuration parameters.
    public synchronized void setConfig(DigesterConfig config) {
        Validate.notNull(config"Config cannot be set null");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = 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 setProvider(java.security.Provider) or setProviderName(java.lang.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.

Parameters:
algorithm the name of the algorithm to be used.
    public synchronized void setAlgorithm(String algorithm) {
        Validate.notEmpty(algorithm"Algorithm cannot be empty");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = algorithm;
        this. = 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.

Parameters:
saltSizeBytes the size of the salt to be used, in bytes.
    public synchronized void setSaltSizeBytes(int saltSizeBytes) {
        Validate.isTrue(saltSizeBytes >= 0, 
                "Salt size in bytes must be non-negative");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = saltSizeBytes;
        this. = (saltSizeBytes > 0);
        this. = 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.

Parameters:
iterations the number of iterations.
    public synchronized void setIterations(int iterations) {
        Validate.isTrue(iterations > 0, 
                "Number of iterations must be greater than zero");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = iterations;
        this. = true;
    }

    
    

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

Parameters:
saltGenerator the salt generator to be used.
Since:
1.2
    public synchronized void setSaltGenerator(SaltGenerator saltGenerator) {
        Validate.notNull(saltGenerator"Salt generator cannot be set null");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = saltGenerator;
        this. = 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 setProvider(java.security.Provider) method, in which case it will not be necessary neither registering the provider beforehand, nor calling this setProviderName(java.lang.String) method to specify a provider name.

Note that a call to setProvider(java.security.Provider) overrides any value set by this method.

If no provider name / provider is explicitly set, the default JVM provider will be used.

Parameters:
providerName the name of the security provider to be asked for the digest algorithm.
Since:
1.3
    public synchronized void setProviderName(String providerName) {
        Validate.notNull(providerName"Provider name cannot be set null");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = providerName;
        this. = 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 setProviderName(java.lang.String) becomes unnecessary.

If no provider name / provider is explicitly set, the default JVM provider will be used.

Parameters:
provider the provider to be asked for the chosen algorithm
Since:
1.3
    public synchronized void setProvider(Provider provider) {
        Validate.notNull(provider"Provider cannot be set null");
        if (isInitialized()) {
            throw new AlreadyInitializedException();
        }
        this. = provider;
        this. = 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.

Returns:
true if the digester has already been initialized, false if not.
    public synchronized boolean isInitialized() {
        return this.;
    }
    

    

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. Then, if a org.jasypt.digest.config.DigesterConfig object has been set with setConfig, the non-null values returned by its getX methods override the default values.
  3. 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 (algorithm, provider, salt size, iterations or salt generator) will result in an AlreadyInitializedException being thrown.

Throws:
org.jasypt.exceptions.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.) {
            /*
             * 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. != null) {
                
                String configAlgorithm = .getAlgorithm();
                if (configAlgorithm != null) {
                    Validate.notEmpty(configAlgorithm
                            "Algorithm cannot be empty");
                }
                
                Integer configSaltSizeBytes = .getSaltSizeBytes();
                if (configSaltSizeBytes != null) {
                    Validate.isTrue(configSaltSizeBytes.intValue() >= 0, 
                            "Salt size in bytes must be non-negative");
                }
                
                Integer configIterations = .getIterations();
                if (configIterations != null) {
                    Validate.isTrue(configIterations.intValue() > 0, 
                            "Number of iterations must be greater than zero");
                }
                
                SaltGenerator configSaltGenerator = .getSaltGenerator();
                
                String configProviderName = .getProviderName();
                if (configProviderName != null) {
                    Validate.notEmpty(configProviderName,
                            "Provider name cannot be empty");
                }
                
                Provider configProvider = .getProvider();
                
                this. = 
                    ((this.) || (configAlgorithm == null))?
                            this. : configAlgorithm;
                this. = 
                    ((this.) || (configSaltSizeBytes == null))?
                            this. : configSaltSizeBytes.intValue();
                this. = 
                    ((this.) || (configIterations == null))?
                            this. : configIterations.intValue();
                this. = 
                    ((this.) || (configSaltGenerator == null))?
                            this. : configSaltGenerator;
                this. = 
                    ((this.) || (configProviderName == null))?
                            this. : configProviderName;
                this. = 
                    ((this.) || (configProvider == null))?
                            this. : configProvider;
                
            }
            
            /*
             * If the digester was not set a salt generator in any way,
             * it is time to apply its default value.
             */
            if (this. == null) {
                this. = new RandomSaltGenerator();
            }
            
            /*
             * 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. != null) {
                    this. = 
                        MessageDigest.getInstance(
                                this.
                                this.);
                } else if (this. != null) {
                    this. = 
                        MessageDigest.getInstance(
                                this.
                                this.);
                } else {
                    this. = MessageDigest.getInstance(this.);
                }
            } catch (NoSuchAlgorithmException e) {
                throw new EncryptionInitializationException(e);
            } catch (NoSuchProviderException e) {
                throw new EncryptionInitializationException(e);
            }
            this. = 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 org.jasypt.salt.SaltGenerator).
  2. The salt bytes are added to the message.
  3. 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).
  4. If specified by the salt generator (see org.jasypt.salt.SaltGenerator.includePlainSaltInEncryptionResults()), the undigested salt and the final result of the hash function are concatenated and returned as a result.
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).

Parameters:
message the byte array to be digested
Returns:
the digest result
Throws:
org.jasypt.exceptions.EncryptionOperationNotPossibleException if the digest operation fails, ommitting any further information about the cause for security reasons.
org.jasypt.exceptions.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.) {
            salt = this..generateSalt(this.);
        }
        // Create digest
        return digest(messagesalt);
        
    }
    
    /*
     * 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(byte[] messagebyte[] salt) {
        
        try {
            
            byte[] encryptedMessage = new byte[0];
            /*
             * Add the salt to the result, as it have to be stored with the 
             * digest itself so that we are able to create a new digest with
             * the same salt for other message and compare them properly.
             */
            if (salt != null) {
                encryptedMessage = ArrayUtils.addAll(encryptedMessagesalt);
            }
            byte[] digest = null;
            
            synchronized (this.) {
                
                this..reset();
                
                if (salt != null) {
                    // The salt is added to the digest
                    this..update(salt);
                }
                this..update(message);
                
                digest = this..digest();
                for (int i = 0; i < (this. - 1); i++) {
                    this..reset();
                    digest = this..digest(digest);
                }
                
            }
            // Finally we build an array containing both the undigested 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..includePlainSaltInEncryptionResults()) {
                encryptedMessage = ArrayUtils.addAll(encryptedMessagedigest);
            } else {
                encryptedMessage = digest;
            }
            
            return encryptedMessage;
        
        } 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.

Parameters:
message the message to be compared to the digest.
digest the digest.
Returns:
true if the specified message matches the digest, false if not.
Throws:
org.jasypt.exceptions.EncryptionOperationNotPossibleException if the digest matching operation fails, ommitting any further information about the cause for security reasons.
org.jasypt.exceptions.EncryptionInitializationException if initialization could not be correctly done (for example, if the digest algorithm chosen cannot be used).
    public boolean matches(byte[] messagebyte[] 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.) {
                // 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..includePlainSaltInEncryptionResults()) {
                    salt = ArrayUtils.subarray(digest, 0, this.);
                } else {
                    salt = this..generateSalt(this.);
                }
            }
            
            // Digest the message with the extracted digest.
            byte[] encryptedMessage = digest(messagesalt);
            
            // If, using the same salt, digests match, then messages too. 
            return (Arrays.equals(encryptedMessagedigest));
        
        } 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();
        }
        
    }
    
    
    
    
New to GrepCode? Check out our FAQ X