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
createSecretKeyis a function that takes parameters likepassword,salt,iterationCount, andkeyLength.passwordis the actual password in the configuration file. In cryptography, asaltis random data that we use as an additional input that hashes data, a password, or a passphrase. The use ofsaltsis to safeguard passwords in storage. We useiterationCountvariable 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. ThekeyLengthvariable is the length of the key that we ultimately need to derive. Throws the exception thrown from the methods used it. - The
getInstancemethod 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 newSecretKeyFactoryobject. It throwsNullPointerExceptionif the specified algorithm is null andNoSuchAlgorithmExceptionif no Provider supports aSecretKeyFactorySpiimplementation for the specified algorithm. PBEKeySpecis a class constructor that takes a password, salt, iteration count, and to-be-derived key length for generatingPBEKeyof variable-key-size PBE ciphers. It throwsNullPointerExceptionifsaltisnullandIllegalArgumentExceptionif salt is empty.generateSecretgenerates aSecretKeyobject from the provided key-specification or the key-material. It takes the specification of the secret key. It throwsInvalidKeySpecExceptionif the given specification is inappropriate for this secret-key factory to produce a classified key value.
Details of encrypt method in Encryption class.
- The
encryptmethod takes two parameters, data to be encrypted and the key. This method throws exceptions thrown from child methods in it. - The
getInstancemethod 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 throwsNoSuchAlgorithmExceptionif a change is null, empty, in an invalid format andNoSuchPaddingExceptionif change contains a padding scheme that is not available. initmethod initializes theCipherfor one of the following four operations: encryption, decryption, key wrapping, or key unwrapping, depending on the operation mode value.ENCRYPT_MODEin our case. The method throwsUnsupportedOperationExceptionif the operation mode is invalid andInvalidKeyExceptionif the given key is inappropriate.- The
getParametersreturns the parameters used with this cipher. - The
getParameterSpecreturns a specification of the parameter object. TheparamSpecparameter identifies the specification class in which the parameters must return. For example, it could be theDSAParameterSpec.classto indicate that the parameters must return in an instance of theDSAParameterSpecclass. doFinalmethod 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.base64Encodeis a private method that encodes the specified byte array into a string using theBase64encoding scheme. The functions used in thedecryptmethod are similar to the above mention method. The only difference is they behave differently based on themodespecified in the function,DECRYPT_MODEas 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