Crypter le mot de passe dans les fichiers de configuration en Java

Rashmi Patidar 12 octobre 2023
Crypter le mot de passe dans les fichiers de configuration en Java

Le cryptage est le processus de transformation d’informations en clair en une forme illisible à l’aide d’un algorithme de cryptage combiné à un paramètre appelé clé de cryptage. Le format illisible est souvent appelé format ciphertext. Seuls ceux qui possèdent la clé de décryptage peuvent décrypter les données et récupérer le texte brut d’origine.

Nous pouvons diviser le problème du chiffrement des mots de passe dans les fichiers de configuration en deux sous-tâches suivantes.

  1. Cryptez le mot de passe en clair qui se trouve dans le fichier.
  2. Décryptez le mot de passe crypté lu à partir du fichier.

Commençons par créer un fichier de configuration nommé fichier config.properties au chemin src/conf/.

password = TestPassword123

Maintenant, pour lire le fichier de configuration, instanciez la classe Properties. Nous pouvons créer une instance de la classe FileInputStream à l’aide de son constructeur. Il prend le chemin du fichier de configuration comme entrée. Maintenant, une instance de la classe de propriétés est utilisée pour charger les propriétés. Utilisez la méthode load pour charger le fichier de propriétés dans la classe, et cela prend l’instance InputStreamReader comme paramètre. Il lance IllegalArgumentException si ce flux d’entrée contient une séquence d’échappement Unicode malformée et IOException si une erreur est survenue lors de la lecture du flux d’entrée.

Une fois les propriétés chargées, utilisez la méthode getProperty() pour rechercher la propriété avec la clé spécifiée dans la liste des propriétés. La méthode renvoie null si elle ne parvient pas à trouver les propriétés. Placez une vérification externe pour gérer une telle situation et lancez IllegalArgumentException si un mot de passe est trouvé nul dans le fichier.

salt est créé avec n’importe quelle chaîne aléatoire à ajouter à la chaîne de mot de passe.

createSecretKey est une méthode définie par l’utilisateur qui renvoie la clé SecretKeySpec, et l’utilisation de la clé consiste à chiffrer et déchiffrer le mot de passe. Les méthodes encrypt et decrypt sont des méthodes static définies par l’usage qui ont été données dans la classe Encryption.

Voici l’exemple de code qui illustre la même chose.

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);
  }
}

Une description détaillée des méthodes définies par l’utilisateur dans la classe Encryption se trouve ci-dessous.

  1. Le createSecretKey est une fonction qui prend des paramètres comme password, salt, iterationCount et keyLength. password est le mot de passe réel dans le fichier de configuration. En cryptographie, un salt est une donnée aléatoire que nous utilisons comme entrée supplémentaire qui hache des données, un mot de passe ou une phrase de passe. L’utilisation de salts consiste à sauvegarder les mots de passe dans le stockage. Nous utilisons la variable iterationCount comme le nombre d’itérations qu’un algorithme doit prendre. Diminuer la valeur de la variable accélère le temps de démarrage et est donc utile pendant les tests, mais cela facilite également la tâche des attaquants par force brute. La variable keyLength est la longueur de la clé que nous devons finalement dériver. Lève l’exception levée à partir des méthodes utilisées.
  2. La méthode getInstance parcourt la liste des fournisseurs de sécurité enregistrés, en commençant par le fournisseur le plus préféré. Il prend le nom standard de l’algorithme de clé secrète demandé et renvoie le nouvel objet SecretKeyFactory. Il lance NullPointerException si l’algorithme spécifié est nul et NoSuchAlgorithmException si aucun fournisseur ne prend en charge une implémentation SecretKeyFactorySpi pour l’algorithme spécifié.
  3. PBEKeySpec est un constructeur de classe qui prend un mot de passe, un sel, un nombre d’itérations et une longueur de clé à dériver pour générer des PBEKey de chiffrements PBE de taille de clé variable. Il lance NullPointerException si salt est null et IllegalArgumentException si salt est vide.
  4. generateSecret génère un objet SecretKey à partir de la spécification de clé fournie ou du matériel de clé. Il prend la spécification de la clé secrète. Il lance InvalidKeySpecException si la spécification donnée est inappropriée pour que cette fabrique de clés secrètes produise une valeur de clé classifiée.

Détails de la méthode encrypt dans la classe Encryption.

  1. La méthode encrypt prend deux paramètres, les données à crypter et la clé. Cette méthode lève des exceptions levées à partir des méthodes enfants qu’elle contient.
  2. La méthode getInstance parcourt la liste des fournisseurs de sécurité enregistrés, en commençant par le fournisseur le plus préféré. Il prend le nom de la transformation, c’est-à-dire AES / CBC / PKCS5Padding. Il lance NoSuchAlgorithmException si un changement est nul, vide, dans un format invalide et NoSuchPaddingException si le changement contient un schéma de remplissage qui n’est pas disponible.
  3. La méthode init initialise le Cipher pour l’une des quatre opérations suivantes: cryptage, décryptage, encapsulation de clé ou déballage de clé, selon la valeur du mode de fonctionnement. ENCRYPT_MODE dans notre cas. La méthode lance UnsupportedOperationException si le mode de fonctionnement n’est pas valide et InvalidKeyException si la clé donnée est inappropriée.
  4. Le getParameters renvoie les paramètres utilisés avec ce chiffrement.
  5. Le getParameterSpec renvoie une spécification de l’objet paramètre. Le paramètre paramSpec identifie la classe de spécification dans laquelle les paramètres doivent retourner. Par exemple, ce pourrait être le DSAParameterSpec.class pour indiquer que les paramètres doivent retourner dans une instance de la classe DSAParameterSpec.
  6. La méthode doFinal crypte ou décrypte les données en une seule pièce de travail ou termine une opération en plusieurs parties. Les données sont chiffrées ou déchiffrées, selon la façon dont nous initialisons le chiffrement.
  7. base64Encode est une méthode privée qui encode le tableau d’octets spécifié dans une chaîne en utilisant le schéma d’encodage Base64. Les fonctions utilisées dans la méthode decrypt sont similaires à la méthode mentionnée ci-dessus. La seule différence est qu’ils se comportent différemment en fonction du mode spécifié dans la fonction, DECRYPT_MODE comme mode de fonctionnement.
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);
  }
}

Vous trouverez ci-dessous la sortie du code écrit pour crypter et décrypter le mot de passe dans le fichier de configuration.

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

Article connexe - Java Encryption