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 StringDigester interface. This class lets the user specify the algorithm to be used for creating digests, the size of the salt to be applied, the number of times the hash function will be applied (iterations) and the salt generator to be used.

This class avoids byte-conversion problems related to the fact of different platforms having different default charsets, and returns digests in the form of BASE64-encoded ASCII Strings.

This class is thread-safe.


Configuration

The algorithm, 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, 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, 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. The String message is converted to a byte array.
  2. A salt of the specified size is generated (see org.jasypt.salt.SaltGenerator).
  3. The salt bytes are added to the message.
  4. 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).
  5. 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.
  6. The result of the concatenation is encoded in BASE64 and returned as an ASCII String.
Put schematically in bytes:
  • DIGEST = |S|..(ssb)..|S|X|X|X|...|X|
    • S: salt bytes (plain, not digested). (OPTIONAL).
    • ssb: salt size in bytes.
    • X: bytes resulting from hashing (see below).
  • |X|X|X|...|X| = H(H(H(..(it)..H(Z|Z|Z|...|Z|))))
    • H: Hash function (algorithm).
    • it: Number of iterations.
    • Z: Input for hashing (see below).
  • |Z|Z|Z|...|Z| = |S|..(ssb)..|S|M|M|M...|M|
    • S: salt bytes (plain, not digested).
    • ssb: salt size in bytes.
    • M: message bytes.
If a random salt generator is used, two digests created for the same message will always be different (except in the case of random salt coincidence). Because of this, in this case the result of the digest method will contain both the undigested salt and the digest of the (salt + message), so that another digest operation can be performed with the same salt on a different message to check if both messages match (all of which will be managed automatically by the matches method).

To learn more about the mechanisms involved in digest creation, read PKCS #5: Password-Based Cryptography Standard.

Author(s):
Daniel Fernández Garrido
Since:
1.0
public final class StandardStringDigester implements StringDigester {

    

Charset to be used to obtain "digestable" byte arrays from input Strings. Set to UTF-8.

This charset has to be fixed to some value so that we avoid problems with different platforms having different "default" charsets.

It is set to UTF-8 because it covers the whole spectrum of characters representable in Java (which internally uses UTF-16), and avoids the size penalty of UTF-16 (which will always use two bytes for representing each character, even if it is an ASCII one).

Setting this value to UTF-8 does not mean that Strings that originally come for, for example, an ISO-8859-1 input, will not be correcly digested. It simply provides a way of "fixing" the way a String will be converted into bytes for digesting.

    public static final String MESSAGE_CHARSET = "UTF-8";
    
    

Charset to be used for encoding the resulting digests. Set to US-ASCII.

The result of digesting some bytes can be any other bytes, and so the result of digesting, for example, some LATIN-1 valid String bytes, can be bytes that may not conform a "valid" LATIN-1 String.

Because of this, digests are always encoded in BASE64 after being created, and this ensures that the digests will make perfectly representable, safe ASCII Strings. Because of this, the charset used to convert the digest bytes to the returned String is set to US-ASCII.

    public static final String DIGEST_CHARSET = "US-ASCII";
    // The StandardByteDigester that will be internally used.
    private StandardByteDigester byteDigester = null;
    
    // BASE64 encoder which will make sure the returned digests are
    // valid US-ASCII strings.
    private Base64 base64 = null;


    
    
Creates a new instance of StandardStringDigester.
    public StandardStringDigester() {
        super();
        this. = new StandardByteDigester();
        this. = new Base64();
    }

    
    

Sets a org.jasypt.digest.config.DigesterConfig object for the digester. If this config object is set, it will be asked values for:

  • Algorithm
  • Salt size
  • Hashing iterations
  • Salt generator

The non-null values it returns will override the default ones (see StandardByteDigester), 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 void setConfig(DigesterConfig config) {
        .setConfig(config);
    }

    
    

Sets the algorithm to be used for hashing, like MD5 or SHA-1.

This algorithm has to be supported by your Java Virtual Machine, and it should be allowed as an algorithm for creating java.security.MessageDigest instances.

Parameters:
algorithm the name of the algorithm to be used. See Appendix A in the Java Cryptography Architecture API Specification & Reference for information about standard algorithm names.
    public void setAlgorithm(String algorithm) {
        .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.

Parameters:
saltSizeBytes the size of the salt to be used, in bytes.
    public void setSaltSizeBytes(int saltSizeBytes) {
        .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.

Parameters:
iterations the number of iterations.
    public void setIterations(int iterations) {
        .setIterations(iterations);
    }

    
    

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.
    public void setSaltGenerator(SaltGenerator saltGenerator) {
        .setSaltGenerator(saltGenerator);
    }

    
    

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, 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 boolean isInitialized() {
        return .isInitialized();
    }

    
    

Initialize the digester.

This operation will consist in determining the actual configuration values to be used, and then initializing the digester with them.
These values are decided by applying the following priorities:

  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, 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 void initialize() {
        .initialize();
    }
    

    

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. A salt of the specified size is generated (see org.jasypt.salt.SaltGenerator).
  3. The salt bytes are added to the message.
  4. 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).
  5. 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.
  6. The result of the concatenation is encoded in BASE64 and returned as an ASCII String.
Put schematically in bytes:
  • DIGEST = |S|..(ssb)..|S|X|X|X|...|X|
    • S: salt bytes (plain, not digested). (OPTIONAL).
    • ssb: salt size in bytes.
    • X: bytes resulting from hashing (see below).
  • |X|X|X|...|X| = H(H(H(..(it)..H(Z|Z|Z|...|Z|))))
    • H: Hash function (algorithm).
    • it: Number of iterations.
    • Z: Input for hashing (see below).
  • |Z|Z|Z|...|Z| = |S|..(ssb)..|S|M|M|M...|M|
    • S: salt bytes (plain, not digested).
    • ssb: salt size in bytes.
    • M: message bytes.

If a random salt generator is used, two digests created for the same message will always be different (except in the case of random salt coincidence). Because of this, in this case the result of the digest method will contain both the undigested salt and the digest of the (salt + message), so that another digest operation can be performed with the same salt on a different message to check if both messages match (all of which will be managed automatically by the matches method).

Parameters:
message the String 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 String digest(String message) {
        
        if (message == null) {
            return null;
        }
        
        try {
            // The input String is converted into bytes using MESSAGE_CHARSET
            // as a fixed charset to avoid problems with different platforms
            // having different default charsets (see MESSAGE_CHARSET doc).
            byte[] messageBytes = message.getBytes();
            // The StandardByteDigester does its job.
            byte[] digest = .digest(messageBytes);
            
            // We encode the result in BASE64 so that we obtain the safest
            // result String possible.
            synchronized () {
                digest = .encode(digest);
            }
            
            // Finally, the result String is encoded in US-ASCII
            return new String(digest);
        } catch (EncryptionInitializationException e) {
            throw e;
        } catch (EncryptionOperationNotPossibleException e) {
            throw e;
        } catch (Exception e) {
            // If digest fails, it is more secure not to return any information
            // about the cause in nested exceptions. Simply fail.
            throw new EncryptionOperationNotPossibleException();
        }
        
    }

    
    
    

Checks a message against a given digest.

This method tells whether a message corresponds to a specific digest or not by getting the salt with which the digest was created and applying it to a digest operation performed on the message. If new and existing digest match, the message is said to match the digest.

This method will be used, for instance, for password checking in authentication processes.

A null message will only match a null digest.

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(String messageString digest) {
        if (message == null) {
            return (digest == null);
        } else if (digest == null) {
            return false;
        }
        
        try {
            
            // We get a valid byte array from the message, in the 
            // fixed MESSAGE_CHARSET that the digest operations use.
            byte[] messageBytes = message.getBytes();
            
            // The digest, which must be a US-ASCII String with BASE64-encoded
            // bytes, is converted into a byte array.
            byte[] digestBytes = digest.getBytes();
            // The BASE64 encoding is reversed.
            synchronized () {
                digestBytes = .decode(digestBytes);
            }
            
            // The StandardByteDigester is asked to match message to digest.
            return .matches(messageBytesdigestBytes); 
        
        } catch (EncryptionInitializationException e) {
            throw e;
        } catch (EncryptionOperationNotPossibleException e) {
            throw e;
        } catch (Exception e) {
            // If digest fails, it is more secure not to return any information
            // about the cause in nested exceptions. Simply fail.
            throw new EncryptionOperationNotPossibleException();
        }
    }
    
    
New to GrepCode? Check out our FAQ X