org.jasypt.encryption.pbe.StandardPBEBigDecimalEncryptor Maven / Gradle / Ivy
/*
* =============================================================================
*
* 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.math.BigDecimal;
import java.math.BigInteger;
import java.security.Provider;
import org.jasypt.commons.CommonUtils;
import org.jasypt.encryption.pbe.config.PBEConfig;
import org.jasypt.exceptions.EncryptionInitializationException;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.jasypt.salt.SaltGenerator;
/**
*
* Standard implementation of the {@link PBEBigDecimalEncryptor} 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.
*
*
* Important: The size of the result of encrypting a number, depending
* on the algorithm, may be much bigger (in bytes) than the size of the
* encrypted number itself. For example, encrypting a 4-byte integer can
* result in an encrypted 16-byte number. This can lead the user into
* problems if the encrypted values are to be stored and not enough room
* has been provided.
*
*
* This class is thread-safe.
*
*
*
Configuration
*
*
* The algorithm, provider, password, key-obtention iterations and salt generator can take
* values in any of these ways:
*
* - Using its default values (except for password).
* - Setting a {@link org.jasypt.encryption.pbe.config.PBEConfig}
* object which provides new
* configuration values.
* - Calling the corresponding setAlgorithm(...),
* setProvider(...), setProviderName(...),
* setPassword(...), setKeyObtentionIterations(...) or
* setSaltGenerator(...) methods.
*
* And the actual values to be used for initialization will be established
* by applying the following priorities:
*
* - First, the default values are considered (except for password).
* - Then, if a {@link org.jasypt.encryption.pbe.config.PBEConfig}
* object has been set with
* setConfig(...), the non-null values returned by its
* getX() methods override the default values.
* - Finally, if the corresponding setX(...) 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.
*
*
*
*
*
Initialization
*
*
* Before it is ready to encrypt, an object of this class has to be
* initialized. Initialization happens:
*
* - When initialize() is called.
* - When encrypt(...) or decrypt(...) are called for the
* first time, if initialize() has not been called before.
*
* Once an encryptor has been initialized, trying to
* change its configuration will
* result in an AlreadyInitializedException being thrown.
*
*
*
*
Usage
*
*
* An encryptor may be used for:
*
* - Encrypting messages, by calling the encrypt(...) method.
* - Decrypting messages, by calling the decrypt(...) method.
*
* 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). 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.
*
*
* To learn more about the mechanisms involved in encryption, read
* PKCS #5: Password-Based Cryptography Standard.
*
*
* @since 1.2
*
* @author Daniel Fernández
*
*/
public final class StandardPBEBigDecimalEncryptor
implements PBEBigDecimalCleanablePasswordEncryptor {
// The StandardPBEByteEncryptor that will be internally used.
private final StandardPBEByteEncryptor byteEncryptor;
/**
* Creates a new instance of StandardPBEBigDecimalEncryptor.
*/
public StandardPBEBigDecimalEncryptor() {
super();
this.byteEncryptor = new StandardPBEByteEncryptor();
}
/*
* Creates a new instance of StandardPBEBigDecimalEncryptor using
* the specified byte digester (constructor used for cloning)
*/
private StandardPBEBigDecimalEncryptor(final StandardPBEByteEncryptor standardPBEByteEncryptor) {
super();
this.byteEncryptor = standardPBEByteEncryptor;
}
/**
*
* Sets a {@link org.jasypt.encryption.pbe.config.PBEConfig} object
* for the encryptor. If this config
* object is set, it will be asked values for:
*
*
*
* - Algorithm
* - Security Provider (or provider name)
* - Password
* - Hashing iterations for obtaining the encryption key
* - 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.
*
*
* @param config the PBEConfig object to be used as the
* source for configuration parameters.
*/
public void setConfig(final PBEConfig config) {
this.byteEncryptor.setConfig(config);
}
/**
*
* Sets the algorithm to be used for encryption, like
* PBEWithMD5AndDES.
*
*
* 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 mode and padding for
* it, like ALGORITHM/MODE/PADDING.
*
*
* @param algorithm the name of the algorithm to be used.
*/
public void setAlgorithm(final String algorithm) {
this.byteEncryptor.setAlgorithm(algorithm);
}
/**
*
* Sets the password to be used.
*
*
* There is no default value for password, so not setting
* this parameter either from a
* {@link org.jasypt.encryption.pbe.config.PBEConfig} object or from
* a call to setPassword will result in an
* EncryptionInitializationException being thrown during initialization.
*
*
* @param password the password to be used.
*/
public void setPassword(final String password) {
this.byteEncryptor.setPassword(password);
}
/**
*
* Sets the password to be used, as a char[].
*
*
* This allows the password to be specified as a cleanable
* 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.
*
*
* Important: 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).
*
*
* There is no default value for password, so not setting
* this parameter either from a
* {@link org.jasypt.encryption.pbe.config.PBEConfig} object or from
* a call to setPassword will result in an
* EncryptionInitializationException being thrown during initialization.
*
*
* @since 1.8
*
* @param password the password to be used.
*/
public void setPasswordCharArray(char[] password) {
this.byteEncryptor.setPasswordCharArray(password);
}
/**
*
* Set the number of hashing iterations applied to obtain the
* encryption key.
*
*
* This mechanism is explained in
* PKCS #5: Password-Based Cryptography Standard.
*
*
* @param keyObtentionIterations the number of iterations
*/
public void setKeyObtentionIterations(final int keyObtentionIterations) {
this.byteEncryptor.setKeyObtentionIterations(keyObtentionIterations);
}
/**
*
* 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 void setSaltGenerator(final SaltGenerator saltGenerator) {
this.byteEncryptor.setSaltGenerator(saltGenerator);
}
/**
*
* 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.
*
*
* 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.
*
*
* @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);
}
/**
*
* 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.
*
*
* 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.
*
*
* @since 1.3
*
* @param provider the provider to be asked for the chosen algorithm
*/
public void setProvider(final Provider provider) {
this.byteEncryptor.setProvider(provider);
}
/*
* Clone this encryptor 'size' times and initialize it.
* This encryptor will be at position 0 itself.
* Clones will NOT be initialized.
*/
synchronized StandardPBEBigDecimalEncryptor[] cloneAndInitializeEncryptor(final int size) {
final StandardPBEByteEncryptor[] byteEncryptorClones =
this.byteEncryptor.cloneAndInitializeEncryptor(size);
final StandardPBEBigDecimalEncryptor[] clones = new StandardPBEBigDecimalEncryptor[size];
clones[0] = this;
for (int i = 1; i < size; i++) {
clones[i] = new StandardPBEBigDecimalEncryptor(byteEncryptorClones[i]);
}
return clones;
}
/**
*
* Returns true if the encryptor has already been initialized, false if
* not.
* Initialization happens:
*
*
* - When initialize is called.
* - When encrypt or decrypt are called for the
* first time, if initialize has not been called before.
*
*
* Once an encryptor has been initialized, trying to
* change its configuration will
* result in an AlreadyInitializedException being thrown.
*
*
* @return true if the encryptor has already been initialized, false if
* not.
*/
public boolean isInitialized() {
return this.byteEncryptor.isInitialized();
}
/**
*
* Initialize the encryptor.
*
*
* This operation will consist in determining the actual configuration
* values to be used, and then initializing the encryptor with them.
*
* These values are decided by applying the following priorities:
*
*
* - First, the default values are considered (except for password).
*
* - Then, if a
* {@link org.jasypt.encryption.pbe.config.PBEConfig}
* object has been set with
* setConfig, the non-null values returned by its
* getX methods override the default values.
* - Finally, if the corresponding setX 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.
*
*
* Once an encryptor 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, no password has been set).
*/
public void initialize() {
this.byteEncryptor.initialize();
}
/**
*
* Encrypts a message using the specified configuration.
*
*
* The resulting
* BigDecimal will have the same scale as the original one (although the
* total number of bytes will be higher).
*
*
* Important: The size of the result of encrypting a number, depending
* on the algorithm, may be much bigger (in bytes) than the size of the
* encrypted number itself. For example, encrypting a 4-byte integer can
* result in an encrypted 16-byte number. This can lead the user into
* problems if the encrypted values are to be stored and not enough room
* has been provided.
*
*
* The mechanisms applied to perform the encryption operation are described
* in PKCS #5: Password-Based Cryptography Standard.
*
*
* 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.
*
*
* 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). 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.
*
*
* @param message the BigDecimal 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 BigDecimal encrypt(final BigDecimal message) {
if (message == null) {
return null;
}
try {
// Get the scale of the decimal number
final int scale = message.scale();
// Get the number in binary form (without scale)
final BigInteger unscaledMessage = message.unscaledValue();
final byte[] messageBytes = unscaledMessage.toByteArray();
// The StandardPBEByteEncryptor does its job.
final byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes);
// The length of the encrypted message will be stored
// with the result itself so that we can correctly rebuild
// the complete byte array when decrypting (BigInteger will
// ignore all "0x0" bytes in the leftmost side, and also "-0x1"
// in the leftmost side will be translated as signum).
final byte[] encryptedMessageLengthBytes =
NumberUtils.byteArrayFromInt(encryptedMessage.length);
// Append the length bytes to the encrypted message
final byte[] encryptionResult =
CommonUtils.appendArrays(encryptedMessage, encryptedMessageLengthBytes);
// Finally, return a new number built from the encrypted bytes
return new BigDecimal(new BigInteger(encryptionResult), scale);
} 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();
}
}
/**
*
* Decrypts a message using the specified configuration.
*
*
* The mechanisms applied to perform the decryption operation are described
* in PKCS #5: Password-Based Cryptography Standard.
*
*
* 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).
*
*
* @param encryptedMessage the BigDecimal 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 BigDecimal decrypt(BigDecimal encryptedMessage) {
if (encryptedMessage == null) {
return null;
}
try {
// Get the scale
int scale = encryptedMessage.scale();
// Get the number (unscaled) in binary form
BigInteger unscaledEncryptedMessage =
encryptedMessage.unscaledValue();
byte[] encryptedMessageBytes =
unscaledEncryptedMessage.toByteArray();
// Process the encrypted byte array (check size, pad if needed...)
encryptedMessageBytes =
NumberUtils.processBigIntegerEncryptedByteArray(
encryptedMessageBytes, encryptedMessage.signum());
// Let the byte encyptor decrypt
byte[] message = this.byteEncryptor.decrypt(encryptedMessageBytes);
// Finally, return a new number built from the decrypted bytes
return new BigDecimal(new BigInteger(message), scale);
} 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();
}
}
}