/*
* =============================================================================
*
* 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.encryption.pbe;
import java.security.Provider;
import org.jasypt.commons.CommonUtils;
import org.jasypt.contrib.org.apache.commons.codec_1_3.binary.Base64;
import org.jasypt.encryption.pbe.config.PBEConfig;
import org.jasypt.encryption.pbe.config.StringPBEConfig;
import org.jasypt.exceptions.AlreadyInitializedException;
import org.jasypt.exceptions.EncryptionInitializationException;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.salt.SaltGenerator;
/**
* <p>
* Standard implementation of the {@link PBEStringEncryptor} interface.
* This class lets the user specify the algorithm (and provider) to be used for
* encryption, the password to use,
* the number of hashing iterations and the salt generator
* that will be applied for obtaining
* the encryption key.
* </p>
* <p>
* This class avoids byte-conversion problems related to the fact of
* different platforms having different default charsets, and returns
* encryption results in the form of BASE64-encoded or HEXADECIMAL
* ASCII Strings.
* </p>
* <p>
* This class is <i>thread-safe</i>.
* </p>
* <p>
* <br/><b><u>Configuration</u></b>
* </p>
* <p>
* The algorithm, provider, password, key-obtention iterations and salt generator can take
* values in any of these ways:
* <ul>
* <li>Using its default values (except for password).</li>
* <li>Setting a <tt>{@link org.jasypt.encryption.pbe.config.PBEConfig}</tt>
* object which provides new
* configuration values.</li>
* <li>Calling the corresponding <tt>setX(...)</tt> methods.</li>
* </ul>
* And the actual values to be used for initialization will be established
* by applying the following priorities:
* <ol>
* <li>First, the default values are considered (except for password).</li>
* <li>Then, if a <tt>{@link org.jasypt.encryption.pbe.config.PBEConfig}</tt>
* object has been set with
* <tt>setConfig(...)</tt>, the non-null values returned by its
* <tt>getX()</tt> methods override the default values.</li>
* <li>Finally, if the corresponding <tt>setX(...)</tt> method has been called
* on the encryptor itself for any of the configuration parameters, the
* values set by these calls override all of the above.</li>
* </ol>
* </p>
*
* <p>
* <br/><b><u>Initialization</u></b>
* </p>
* <p>
* Before it is ready to encrypt, an object of this class has to be
* <i>initialized</i>. Initialization happens:
* <ul>
* <li>When <tt>initialize()</tt> is called.</li>
* <li>When <tt>encrypt(...)</tt> or <tt>decrypt(...)</tt> are called for the
* first time, if <tt>initialize()</tt> has not been called before.</li>
* </ul>
* Once an encryptor has been initialized, trying to
* change its configuration will
* result in an <tt>AlreadyInitializedException</tt> being thrown.
* </p>
*
* <p>
* <br/><b><u>Usage</u></b>
* </p>
* <p>
* An encryptor may be used for:
* <ul>
* <li><i>Encrypting messages</i>, by calling the <tt>encrypt(...)</tt> method.</li>
* <li><i>Decrypting messages</i>, by calling the <tt>decrypt(...)</tt> method.</li>
* </ul>
* <b>If a random salt generator is used, two encryption results for
* the same message will always be different
* (except in the case of random salt coincidence)</b>. This may enforce
* security by difficulting brute force attacks on sets of data at a time
* and forcing attackers to perform a brute force attack on each separate
* piece of encrypted data.
* </p>
* <p>
* To learn more about the mechanisms involved in encryption, read
* <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2127"
* target="_blank">PKCS #5: Password-Based Cryptography Standard</a>.
* </p>
*
* @since 1.0
*
* @author Daniel Fernández
*
*/
public final class StandardPBEStringEncryptor implements PBEStringCleanablePasswordEncryptor {
/**
* <p>
* Charset to be used to obtain "encryptable" byte arrays from input
* Strings. <b>Set to UTF-8</b>.
* </p>
* <p>
* This charset has to be fixed to some value so that we avoid problems
* with different platforms having different "default" charsets.
* </p>
* <p>
* It is set to <b>UTF-8</b> 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).
* </p>
* <p>
* Setting it to UTF-8 does not mean that Strings that originally come,
* for example, from an ISO-8859-1 input, won't be correctly encoded, as we
* only need to use the same charset both when encoding and decoding. That
* way the same String will be reconstructed independently of the original
* encoding (for encrypting, we only need "a byte representation" of the
* string, not "a readable byte representation").
* </p>
*/
private static final String MESSAGE_CHARSET = "UTF-8";
/**
* <p>
* Charset to be used for encoding the encryption results.
* Set to <b>US-ASCII</b>.
* </p>
* <p>
* The result of encrypting some bytes can be any other bytes, and so
* the result of encrypting, for example, some LATIN-1 valid String bytes,
* can be bytes that may not conform a "valid" LATIN-1 String.
* </p>
* <p>
* Because of this, encryption results are always encoded in <i>BASE64</i>
* (default) or <i>HEXADECIMAL</i> after being created, and this ensures
* that the results will make perfectly representable, safe ASCII Strings.
* Because of this, the charset used to convert the encrypted bytes to the
* returned String is set to <b>US-ASCII</b>.
* </p>
*/
private static final String ENCRYPTED_MESSAGE_CHARSET = "US-ASCII";
/**
* <p>
* Default type of String output. Set to <b>BASE64</b>.
* </p>
*/
public static final String DEFAULT_STRING_OUTPUT_TYPE =
CommonUtils.STRING_OUTPUT_TYPE_BASE64;
// If the config object set is a StringPBEConfig, it must be referenced
private StringPBEConfig stringPBEConfig = null;
// This variable holds the type of String output which will be done,
// and also a boolean variable for faster comparison
private String stringOutputType = DEFAULT_STRING_OUTPUT_TYPE;
private boolean stringOutputTypeBase64 = true;
/*
* 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 stringOutputTypeSet = false;
// The StandardPBEByteEncryptor that will be internally used.
private final StandardPBEByteEncryptor byteEncryptor;
// BASE64 encoder which will make sure the returned results are
// valid US-ASCII strings.
// The Base64 encoder is THREAD-SAFE
private final Base64 base64;
/**
* Creates a new instance of <tt>StandardPBEStringEncryptor</tt>.
*/
public StandardPBEStringEncryptor() {
super();
this.byteEncryptor = new StandardPBEByteEncryptor();
this.base64 = new Base64();
}
/*
* Creates a new instance of <tt>StandardPBEStringEncryptor</tt> using
* the specified byte encryptor (constructor used for cloning)
*/
private StandardPBEStringEncryptor(final StandardPBEByteEncryptor standardPBEByteEncryptor) {
super();
this.byteEncryptor = standardPBEByteEncryptor;
this.base64 = new Base64();
}
/**
* <p>
* Sets a <tt>{@link org.jasypt.encryption.pbe.config.PBEConfig}</tt> object
* for the encryptor. If this config
* object is set, it will be asked values for:
* </p>
*
* <ul>
* <li>Algorithm</li>
* <li>Security Provider (or provider name)</li>
* <li>Password</li>
* <li>Hashing iterations for obtaining the encryption key</li>
* <li>Salt generator</li>
* <li>Output type (base64, hexadecimal)
* (only <tt>StringPBEConfig</tt>)</li>
* </ul>
*
* <p>
* The non-null values it returns will override the default ones,
* <i>and will be overriden by any values specified with a <tt>setX</tt>
* method</i>.
* </p>
*
* @param config the <tt>PBEConfig</tt> object to be used as the
* source for configuration parameters.
*/
public synchronized void setConfig(final PBEConfig config) {
this.byteEncryptor.setConfig(config);
if ((config != null) && (config instanceof StringPBEConfig)) {
this.stringPBEConfig = (StringPBEConfig) config;
}
}
/**
* <p>
* Sets the algorithm to be used for encryption, like
* <tt>PBEWithMD5AndDES</tt>.
* </p>
* <p>
* This algorithm has to be supported by your JCE provider (if you specify
* one, or the default JVM provider if you don't) and, if it is supported,
* you can also specify <i>mode</i> and <i>padding</i> for
* it, like <tt>ALGORITHM/MODE/PADDING</tt>.
* </p>
*
* @param algorithm the name of the algorithm to be used.
*/
public void setAlgorithm(final String algorithm) {
this.byteEncryptor.setAlgorithm(algorithm);
}
/**
* <p>
* Sets the password to be used.
* </p>
* <p>
* <b>There is no default value for password</b>, so not setting
* this parameter either from a
* {@link org.jasypt.encryption.pbe.config.PBEConfig} object or from
* a call to <tt>setPassword</tt> will result in an
* EncryptionInitializationException being thrown during initialization.
* </p>
*
* @param password the password to be used.
*/
public void setPassword(final String password) {
this.byteEncryptor.setPassword(password);
}
/**
* <p>
* Sets the password to be used, as a char[].
* </p>
* <p>
* This allows the password to be specified as a <i>cleanable</i>
* char[] instead of a String, in extreme security conscious environments
* in which no copy of the password as an immutable String should
* be kept in memory.
* </p>
* <p>
* <b>Important</b>: the array specified as a parameter WILL BE COPIED
* in order to be stored as encryptor configuration. The caller of
* this method will therefore be responsible for its cleaning (jasypt
* will only clean the internally stored copy).
* </p>
* <p>
* <b>There is no default value for password</b>, so not setting
* this parameter either from a
* {@link org.jasypt.encryption.pbe.config.PBEConfig} object or from
* a call to <tt>setPassword</tt> will result in an
* EncryptionInitializationException being thrown during initialization.
* </p>
*
* @since 1.8
*
* @param password the password to be used.
*/
public void setPasswordCharArray(char[] password) {
this.byteEncryptor.setPasswordCharArray(password);
}
/**
* <p>
* Set the number of hashing iterations applied to obtain the
* encryption key.
* </p>
* <p>
* This mechanism is explained in
* <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2127"
* target="_blank">PKCS #5: Password-Based Cryptography Standard</a>.
* </p>
*
* @param keyObtentionIterations the number of iterations
*/
public void setKeyObtentionIterations(final int keyObtentionIterations) {
this.byteEncryptor.setKeyObtentionIterations(keyObtentionIterations);
}
/**
* <p>
* Sets the salt generator to be used. If no salt generator is specified,
* an instance of {@link org.jasypt.salt.RandomSaltGenerator} will be used.
* </p>
*
* @param saltGenerator the salt generator to be used.
*/
public void setSaltGenerator(final SaltGenerator saltGenerator) {
this.byteEncryptor.setSaltGenerator(saltGenerator);
}
/**
* <p>
* Sets the name of the security provider to be asked for the
* encryption algorithm. This security provider has to be registered
* beforehand at the JVM security framework.
* </p>
* <p>
* 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.
* </p>
* <p>
* Note that a call to {@link #setProvider(Provider)} overrides any value
* set by this method.
* </p>
* <p>
* If no provider name / provider is explicitly set, the default JVM
* provider will be used.
* </p>
*
* @since 1.3
*
* @param providerName the name of the security provider to be asked
* for the encryption algorithm.
*/
public void setProviderName(final String providerName) {
this.byteEncryptor.setProviderName(providerName);
}
/**
* <p>
* Sets the security provider to be asked for the encryption 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.
* </p>
* <p>
* If this method is called, calling {@link #setProviderName(String)}
* becomes unnecessary.
* </p>
* <p>
* If no provider name / provider is explicitly set, the default JVM
* provider will be used.
* </p>
*
* @since 1.3
*
* @param provider the provider to be asked for the chosen algorithm
*/
public void setProvider(final Provider provider) {
this.byteEncryptor.setProvider(provider);
}
/**
* <p>
* Sets the the form in which String output
* will be encoded. Available encoding types are:
* </p>
* <ul>
* <li><tt><b>base64</b></tt> (default)</li>
* <li><tt><b>hexadecimal</b></tt></li>
* </ul>
* <p>
* If not set, null will be returned.
* </p>
* @since 1.3
*
* @param stringOutputType the string output type.
*/
public synchronized void setStringOutputType(final String stringOutputType) {
CommonUtils.validateNotEmpty(stringOutputType,
"String output type cannot be set empty");
if (isInitialized()) {
throw new AlreadyInitializedException();
}
this.stringOutputType =
CommonUtils.
getStandardStringOutputType(stringOutputType);
this.stringOutputTypeSet = true;
}
/*
* Clone this encryptor 'size' times and initialize it.
* This encryptor will be at position 0 itself.
* Clones will NOT be initialized.
*/
synchronized StandardPBEStringEncryptor[] cloneAndInitializeEncryptor(final int size) {
final StandardPBEByteEncryptor[] byteEncryptorClones =
this.byteEncryptor.cloneAndInitializeEncryptor(size);
initializeSpecifics();
final StandardPBEStringEncryptor[] clones = new StandardPBEStringEncryptor[size];
clones[0] = this;
for (int i = 1; i < size; i++) {
clones[i] = new StandardPBEStringEncryptor(byteEncryptorClones[i]);
if (CommonUtils.isNotEmpty(this.stringOutputType)) {
clones[i].setStringOutputType(this.stringOutputType);
}
}
return clones;
}
/**
* <p>
* Returns true if the encryptor has already been initialized, false if
* not.<br/>
* Initialization happens:
* </p>
* <ul>
* <li>When <tt>initialize</tt> is called.</li>
* <li>When <tt>encrypt</tt> or <tt>decrypt</tt> are called for the
* first time, if <tt>initialize</tt> has not been called before.</li>
* </ul>
* <p>
* Once an encryptor has been initialized, trying to
* change its configuration will
* result in an <tt>AlreadyInitializedException</tt> being thrown.
* </p>
*
* @return true if the encryptor has already been initialized, false if
* not.
*/
public boolean isInitialized() {
return this.byteEncryptor.isInitialized();
}
/**
* <p>
* Initialize the encryptor.
* </p>
* <p>
* This operation will consist in determining the actual configuration
* values to be used, and then initializing the encryptor with them.
* <br/>
* These values are decided by applying the following priorities:
* </p>
* <ol>
* <li>First, the default values are considered (except for password).
* </li>
* <li>Then, if a
* <tt>{@link org.jasypt.encryption.pbe.config.PBEConfig}</tt>
* object has been set with
* <tt>setConfig</tt>, the non-null values returned by its
* <tt>getX</tt> methods override the default values.</li>
* <li>Finally, if the corresponding <tt>setX</tt> method has been called
* on the encryptor itself for any of the configuration parameters,
* the values set by these calls override all of the above.</li>
* </ol>
* <p>
* Once an encryptor has been initialized, trying to
* change its configuration will
* result in an <tt>AlreadyInitializedException</tt> being thrown.
* </p>
*
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, no password has been set).
*/
public synchronized void initialize() {
// Double-check to avoid synchronization issues
if (!this.isInitialized()) {
initializeSpecifics();
this.byteEncryptor.initialize();
}
}
private void initializeSpecifics() {
/*
* If a StringPBEConfig 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.stringPBEConfig != null) {
final String configStringOutputType =
this.stringPBEConfig.getStringOutputType();
this.stringOutputType =
((this.stringOutputTypeSet) || (configStringOutputType == null))?
this.stringOutputType : configStringOutputType;
}
this.stringOutputTypeBase64 =
(CommonUtils.STRING_OUTPUT_TYPE_BASE64.
equalsIgnoreCase(this.stringOutputType));
}
/**
* <p>
* Encrypts a message using the specified configuration.
* </p>
* </p>
* The Strings returned by this method are BASE64-encoded (default) or
* HEXADECIMAL ASCII Strings.
* </p>
* <p>
* The mechanisms applied to perform the encryption operation are described
* in <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2127"
* target="_blank">PKCS #5: Password-Based Cryptography Standard</a>.
* </p>
* <p>
* This encryptor uses a salt for each encryption
* operation. The size of the salt depends on the algorithm
* being used. This salt is used
* for creating the encryption key and, if generated by a random generator,
* it is also appended unencrypted at the beginning
* of the results so that a decryption operation can be performed.
* </p>
* <p>
* <b>If a random salt generator is used, two encryption results for
* the same message will always be different
* (except in the case of random salt coincidence)</b>. This may enforce
* security by difficulting brute force attacks on sets of data at a time
* and forcing attackers to perform a brute force attack on each separate
* piece of encrypted data.
* </p>
*
* @param message the String message to be encrypted
* @return the result of encryption
* @throws EncryptionOperationNotPossibleException if the encryption
* operation fails, ommitting any further information about the
* cause for security reasons.
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, no password has been set).
*/
public String encrypt(final String message) {
if (message == null) {
return null;
}
// Check initialization
if (!isInitialized()) {
initialize();
}
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).
final byte[] messageBytes = message.getBytes(MESSAGE_CHARSET);
// The StandardPBEByteEncryptor does its job.
byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes);
// We encode the result in BASE64 or HEXADECIMAL so that we obtain
// the safest result String possible.
String result = null;
if (this.stringOutputTypeBase64) {
encryptedMessage = this.base64.encode(encryptedMessage);
result = new String(encryptedMessage,ENCRYPTED_MESSAGE_CHARSET);
} else {
result = CommonUtils.toHexadecimal(encryptedMessage);
}
return result;
} catch (EncryptionInitializationException e) {
throw e;
} catch (EncryptionOperationNotPossibleException e) {
throw e;
} catch (Exception e) {
// If encryption fails, it is more secure not to return any
// information about the cause in nested exceptions. Simply fail.
throw new EncryptionOperationNotPossibleException();
}
}
/**
* <p>
* Decrypts a message using the specified configuration.
* </p>
* <p>
* This method expects to receive a BASE64-encoded (default)
* or HEXADECIMAL ASCII String.
* </p>
* <p>
* The mechanisms applied to perform the decryption operation are described
* in <a href="http://www.rsasecurity.com/rsalabs/node.asp?id=2127"
* target="_blank">PKCS #5: Password-Based Cryptography Standard</a>.
* </p>
* <p>
* If a random salt generator is used, this decryption operation will
* expect to find an unencrypted salt at the
* beginning of the encrypted input, so that the decryption operation can be
* correctly performed (there is no other way of knowing it).
* </p>
*
* @param encryptedMessage the String message to be decrypted
* @return the result of decryption
* @throws EncryptionOperationNotPossibleException if the decryption
* operation fails, ommitting any further information about the
* cause for security reasons.
* @throws EncryptionInitializationException if initialization could not
* be correctly done (for example, no password has been set).
*/
public String decrypt(final String encryptedMessage) {
if (encryptedMessage == null) {
return null;
}
// Check initialization
if (!isInitialized()) {
initialize();
}
try {
byte[] encryptedMessageBytes = null;
// Decode input to bytes depending on whether it is a
// BASE64-encoded or hexadecimal String
if (this.stringOutputTypeBase64) {
encryptedMessageBytes =
encryptedMessage.getBytes(ENCRYPTED_MESSAGE_CHARSET);
encryptedMessageBytes =
this.base64.decode(encryptedMessageBytes);
} else {
encryptedMessageBytes =
CommonUtils.fromHexadecimal(encryptedMessage);
}
// Let the byte encyptor decrypt
final byte[] message = this.byteEncryptor.decrypt(encryptedMessageBytes);
// Return the resulting decrypted String, using MESSAGE_CHARSET
// as charset to maintain between encryption and decyption
// processes.
return new String(message, MESSAGE_CHARSET);
} catch (EncryptionInitializationException e) {
throw e;
} catch (EncryptionOperationNotPossibleException e) {
throw e;
} catch (Exception e) {
// If decryption fails, it is more secure not to return any
// information about the cause in nested exceptions. Simply fail.
throw new EncryptionOperationNotPossibleException();
}
}
}