How to Encrypt Password in Configuration Files in Java
Encryption is the process of transforming plaintext information into an unreadable form using an encryption algorithm combined with a parameter called encryption key
. The unreadable format is often known as the ciphertext
format. Only those who possess the decryption key
can decrypt the data and recover the original plaintext.
We can break the problem of encrypting passwords in configuration files into two following sub-tasks.
- Encrypt the plaintext password that is there in the file.
- Decrypt the encrypted password read in from the file.
Let us first make a configuration file named config.properties
file at the src/conf/
path.
password = TestPassword123
Now to read the configuration file, instantiate the Properties
class. We can create an instance of the FileInputStream
class using its constructor. It takes the path of the configuration file as its input. Now an instance of properties class is used to load the properties. Use the load
method for loading the properties file in the class, and this takes the InputStreamReader
instance as a parameter. It throws IllegalArgumentException
if this input stream contains a malformed Unicode escape sequence and IOException
if an error occurred when reading from the input stream.
Once the properties are successfully loaded, use the getProperty()
method to search for the property with the specified key in the property list. The method returns null
if it is unable to find the properties. Place an external check to handle such a situation and throw IllegalArgumentException
if a password is found null from the file.
salt
is created with any random String to add to the password string.
createSecretKey
is a user-defined method that returns the SecretKeySpec
key, and the use of the key is to encrypt and decrypt the password. encrypt
and decrypt
methods are used-defined static
methods that have been given, in the Encryption
class.
Below is the sample code that demonstrates the same.
package fileDataEncryption;
import static fileDataEncryption.Encryption.*;
import java.io.FileInputStream;
import java.util.Properties;
import javax.crypto.spec.SecretKeySpec;
public class ConfigFileEncryption {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
FileInputStream inputStream = new FileInputStream("src/conf/config.properties");
properties.load(inputStream);
String password = properties.getProperty("password");
if (password == null) {
throw new IllegalArgumentException("No such parameter present in config file");
}
byte[] salt = new String("12345678").getBytes();
int iterationCount = 40000;
int keyLength = 128;
SecretKeySpec key = createSecretKey(password.toCharArray(), salt, iterationCount, keyLength);
String originalPassword = password;
System.out.println("Original password: " + originalPassword);
String encryptedPassword = encrypt(originalPassword, key);
System.out.println("Encrypted password: " + encryptedPassword);
String decryptedPassword = decrypt(encryptedPassword, key);
System.out.println("Decrypted password: " + decryptedPassword);
}
}
A detailed description of user-defined methods in the Encryption
class is as below.
- The
createSecretKey
is a function that takes parameters likepassword
,salt
,iterationCount
, andkeyLength
.password
is the actual password in the configuration file. In cryptography, asalt
is random data that we use as an additional input that hashes data, a password, or a passphrase. The use ofsalts
is to safeguard passwords in storage. We useiterationCount
variable as the number of iterations that an Algorithm should take. Decreasing the value of the variable speeds down the start-up time and hence are helpful during testing, but it also makes it easier for brute force attackers. ThekeyLength
variable is the length of the key that we ultimately need to derive. Throws the exception thrown from the methods used it. - The
getInstance
method traverses the list of registered security Providers, starting with the most preferred Provider. It takes the standard name of the requested secret-key algorithm and returns the newSecretKeyFactory
object. It throwsNullPointerException
if the specified algorithm is null andNoSuchAlgorithmException
if no Provider supports aSecretKeyFactorySpi
implementation for the specified algorithm. PBEKeySpec
is a class constructor that takes a password, salt, iteration count, and to-be-derived key length for generatingPBEKey
of variable-key-size PBE ciphers. It throwsNullPointerException
ifsalt
isnull
andIllegalArgumentException
if salt is empty.generateSecret
generates aSecretKey
object from the provided key-specification or the key-material. It takes the specification of the secret key. It throwsInvalidKeySpecException
if the given specification is inappropriate for this secret-key factory to produce a classified key value.
Details of encrypt
method in Encryption
class.
- The
encrypt
method takes two parameters, data to be encrypted and the key. This method throws exceptions thrown from child methods in it. - The
getInstance
method traverses the list of registered security Providers, starting with the most preferred Provider. It takes the name of the transformation, that is AES/CBC/PKCS5Padding. It throwsNoSuchAlgorithmException
if a change is null, empty, in an invalid format andNoSuchPaddingException
if change contains a padding scheme that is not available. init
method initializes theCipher
for one of the following four operations: encryption, decryption, key wrapping, or key unwrapping, depending on the operation mode value.ENCRYPT_MODE
in our case. The method throwsUnsupportedOperationException
if the operation mode is invalid andInvalidKeyException
if the given key is inappropriate.- The
getParameters
returns the parameters used with this cipher. - The
getParameterSpec
returns a specification of the parameter object. TheparamSpec
parameter identifies the specification class in which the parameters must return. For example, it could be theDSAParameterSpec.class
to indicate that the parameters must return in an instance of theDSAParameterSpec
class. doFinal
method encrypts or decrypts data in a single-part working or finishes a multiple-part operation. The data is encrypted or decrypted, depending on how we initialize the cipher.base64Encode
is a private method that encodes the specified byte array into a string using theBase64
encoding scheme. The functions used in thedecrypt
method are similar to the above mention method. The only difference is they behave differently based on themode
specified in the function,DECRYPT_MODE
as an operation mode.
package fileDataEncryption;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Encryption {
public static SecretKeySpec createSecretKey(char[] password, byte[] salt, int iterationCount,
int keyLength) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec keySpec = new PBEKeySpec(password, salt, iterationCount, keyLength);
SecretKey keyTmp = keyFactory.generateSecret(keySpec);
return new SecretKeySpec(keyTmp.getEncoded(), "AES");
}
public static String encrypt(String dataToEncrypt, SecretKeySpec key)
throws GeneralSecurityException, UnsupportedEncodingException {
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters parameters = pbeCipher.getParameters();
IvParameterSpec ivParameterSpec = parameters.getParameterSpec(IvParameterSpec.class);
byte[] cryptoText = pbeCipher.doFinal(dataToEncrypt.getBytes("UTF-8"));
byte[] iv = ivParameterSpec.getIV();
return base64Encode(iv) + ":" + base64Encode(cryptoText);
}
private static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
public static String decrypt(String string, SecretKeySpec key)
throws GeneralSecurityException, IOException {
String iv = string.split(":")[0];
String property = string.split(":")[1];
Cipher pbeCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
pbeCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(base64Decode(iv)));
return new String(pbeCipher.doFinal(base64Decode(property)), "UTF-8");
}
private static byte[] base64Decode(String property) throws IOException {
return Base64.getDecoder().decode(property);
}
}
Below is the output of the code written to encrypt and decrypt the password in the configuration file.
Original password: TestPassword123
Encrypted password: Hy7fbIwpyKgp0oileu+oLg==:WNRknMJz/8u8GmWlCZFPFA==
Decrypted password: TestPassword123
Rashmi is a professional Software Developer with hands on over varied tech stack. She has been working on Java, Springboot, Microservices, Typescript, MySQL, Graphql and more. She loves to spread knowledge via her writings. She is keen taking up new things and adopt in her career.
LinkedIn