Cifrar contraseña en archivos de configuración en Java

Rashmi Patidar 12 octubre 2023
Cifrar contraseña en archivos de configuración en Java

El cifrado es el proceso de transformar información en texto plano en una forma ilegible utilizando un algoritmo de cifrado combinado con un parámetro llamado clave de cifrado. El formato ilegible se conoce a menudo como formato de texto cifrado. Solo aquellos que poseen la clave de descifrado pueden descifrar los datos y recuperar el texto sin formato original.

Podemos dividir el problema de cifrar las contraseñas en los archivos de configuración en dos subtareas siguientes.

  1. Cifre la contraseña de texto sin formato que se encuentra en el archivo.
  2. Descifre la contraseña encriptada leída del archivo.

Primero hagamos un archivo de configuración llamado archivo config.properties en la ruta src/conf/.

password = TestPassword123

Ahora, para leer el archivo de configuración, cree una instancia de la clase Properties. Podemos crear una instancia de la clase FileInputStream usando su constructor. Toma la ruta del archivo de configuración como entrada. Ahora se usa una instancia de la clase de propiedades para cargar las propiedades. Utilice el método load para cargar el archivo de propiedades en la clase, y esto toma la instancia InputStreamReader como parámetro. Lanza IllegalArgumentException si este flujo de entrada contiene una secuencia de escape Unicode mal formada y IOException si ocurrió un error al leer del flujo de entrada.

Una vez que las propiedades se hayan cargado correctamente, utilice el método getProperty() para buscar la propiedad con la clave especificada en la lista de propiedades. El método devuelve null si no puede encontrar las propiedades. Coloque una verificación externa para manejar tal situación y arroje IllegalArgumentException si una contraseña se encuentra nula en el archivo.

salt se crea con cualquier cadena aleatoria para agregar a la cadena de contraseña.

createSecretKey es un método definido por el usuario que devuelve la clave SecretKeySpec, y el uso de la clave es para cifrar y descifrar la contraseña. Los métodos encrypt y decrypt son métodos estáticos definidos por el uso que se han proporcionado en la clase Encryption.

A continuación se muestra el código de muestra que demuestra lo mismo.

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 continuación se ofrece una descripción detallada de los métodos definidos por el usuario en la clase Encryption.

  1. createSecretKey es una función que toma parámetros como password, salt, iterationCount y keyLength. Contraseña es la contraseña real en el archivo de configuración. En criptografía, un salt son datos aleatorios que usamos como una entrada adicional que codifica datos, una contraseña o una frase de contraseña. El uso de salts es para salvaguardar las contraseñas almacenadas. Usamos la variable iterationCount como el número de iteraciones que debe tomar un algoritmo. Disminuir el valor de las velocidades variables durante el tiempo de inicio y, por lo tanto, es útil durante las pruebas, pero también lo hace más fácil para los atacantes de fuerza bruta. La variable keyLength es la longitud de la clave que finalmente necesitamos derivar. Lanza la excepción lanzada por los métodos que la usaron.
  2. El método getInstance recorre la lista de proveedores de seguridad registrados, comenzando por el proveedor más preferido. Toma el nombre estándar del algoritmo de clave secreta solicitado y devuelve el nuevo objeto SecretKeyFactory. Lanza NullPointerException si el algoritmo especificado es nulo y NoSuchAlgorithmException si ningún proveedor admite una implementación de SecretKeyFactorySpi para el algoritmo especificado.
  3. PBEKeySpec es un constructor de clases que toma una contraseña, sal, conteo de iteraciones y longitud de clave a derivar para generar PBEKey de cifrados PBE de tamaño de clave variable. Lanza NullPointerException si salt es null y IllegalArgumentException si salt está vacío.
  4. generateSecret genera un objeto SecretKey a partir de la especificación de clave proporcionada o del material de clave. Toma la especificación de la clave secreta. Lanza InvalidKeySpecException si la especificación dada es inapropiada para que esta fábrica de claves secretas produzca un valor de clave clasificada.

Detalles del método encrypt en la clase Encryption.

  1. El método encrypt toma dos parámetros, los datos a encriptar y la clave. Este método arroja excepciones lanzadas desde métodos secundarios en él.
  2. El método getInstance recorre la lista de proveedores de seguridad registrados, comenzando por el proveedor más preferido. Toma el nombre de la transformación, es decir AES / CBC / PKCS5Padding. Lanza NoSuchAlgorithmException si un cambio es nulo, vacío, en un formato no válido y NoSuchPaddingException si el cambio contiene un esquema de relleno que no está disponible.
  3. El método init inicializa el Cipher para una de las siguientes cuatro operaciones: cifrado, descifrado, encapsulado de claves o desencriptado de claves, según el valor del modo de operación. ENCRYPT_MODE en nuestro caso. El método arroja UnsupportedOperationException si el modo de operación no es válido y InvalidKeyException si la clave dada es inapropiada.
  4. El getParameters devuelve los parámetros utilizados con este cifrado.
  5. El getParameterSpec devuelve una especificación del objeto de parámetro. El parámetro paramSpec identifica la clase de especificación en la que deben regresar los parámetros. Por ejemplo, podría ser la clase DSAParameterSpec.class para indicar que los parámetros deben regresar en una instancia de la clase DSAParameterSpec.
  6. El método doFinal cifra o descifra datos en un trabajo de una sola parte o finaliza una operación de varias partes. Los datos se cifran o descifran, dependiendo de cómo inicialicemos el cifrado.
  7. base64Encode es un método privado que codifica el array de bytes especificada en una cadena utilizando el esquema de codificación Base64. Las funciones utilizadas en el método decrypt son similares al método mencionado anteriormente. La única diferencia es que se comportan de manera diferente según el mode especificado en la función, DECRYPT_MODE como modo de operación.
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);
  }
}

A continuación se muestra la salida del código escrito para cifrar y descifrar la contraseña en el archivo de configuración.

Original password: TestPassword123
Encrypted password: Hy7fbIwpyKgp0oileu+oLg==:WNRknMJz/8u8GmWlCZFPFA==
Decrypted password: TestPassword123
Rashmi Patidar avatar Rashmi Patidar avatar

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

Artículo relacionado - Java Encryption