Introduction
Chiffrer et déchiffrer des informations est un besoin qu’on rencontre souvent dans nos projets et Java donne nativement une palette d’outils pour y arriver. On va voir dans cet article comment réaliser ces deux opérations via un cas pratique.
L’exemple sera très simple, j’ai un mot de passe, je le chiffre en AES puis je le déchiffre.
I. Chiffrer des informations
Pour chiffrer des informations, il faut déclarer un chiffrement (Cipher) qui peut prendre comme paramètre :
– un algorithme (AES, SHA256, MD5..)
– le trio « algorithme / mode d’opération/ padding »
En 2 mots :
– L’algorithme c’est “comment je chiffre un bloc de bytes de longueur fixe”
– Le mode d’opération c’est “comment j’applique l’algorithme à un groupe de blocs”
– Le padding c’est “comment je remplis les informations de mes blocs quand ils ne sont pas remplis”
Pour comprendre le padding, je vous conseille ce site que j’ai trouvé top https://asecuritysite.com/symmetric/padding
Exemple
Cipher.getInstance("AES");
ou
Ensuite, il faut générer une clef et mettre le notre Cipher en mode “chiffrement”.
Si je prends l’algorithme AES
Je génère ma clef:
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
// La méthode init prend en paramètre la taille de la clef, ici je choisis 256 bits
keyGenerator.init(256);
Note : Il existe d’autres manières de générer une clef (à partir d’un mot de passe par exemple).
Je la transmet à mon Cipher en lui passant comme paramètre le mode “chiffrement”
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
Ensuite, je dis à mon Cipher de chiffrer un tableau de bytes (comme on chiffre par bloc, cf. https://fr.wikipedia.org/wiki/Chiffrement_par_bloc) avec la méthode doFinal()
String password = "M0nSup3rP4ssw0rd";
byte[] encryptedPassword = cipher.doFinal(password.getBytes(StandardCharsets.UTF_8));
A ce stade, on a un mot de passe chiffré.
II. Déchiffrer le mot de passe
On reprend ce qui a été fait précédemment sauf qu’on change le mode de notre Cipher en
Cipher.DECRYPT_MODE
S’il a la même clef, il va être en mesure de déchiffrer notre mot de passe:
String decryptedPassword = new String(cipher.doFinal(encryptedPassword));
Le code final
import org.junit.jupiter.api.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import static org.assertj.core.api.Assertions.assertThat;
public class EncryptionTest {
@Test
void shouldCipherAndDecipher() throws Exception {
String password = "M0nSup3rP4ssw0rd";
SecretKey secretKey = getSecretKey();
byte[] encryptedPassword = encrypt(password.getBytes(StandardCharsets.UTF_8), secretKey);
String decryptedPassword = decrypt(encryptedPassword, secretKey);
assertThat(decryptedPassword).isEqualTo(password);
}
private SecretKey getSecretKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
return keyGenerator.generateKey();
}
private byte[] encrypt(byte[] input, SecretKey secretKey) throws Exception {
Cipher cipher = getCipher();
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(input);
}
private String decrypt(byte[] input, SecretKey secretKey) throws Exception {
Cipher cipher = getCipher();
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(input), StandardCharsets.UTF_8);
}
private Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
return Cipher.getInstance("AES/ECB/PKCS5Padding");
}
}
Pour aller plus loin
La sélection du provider se fait suivant un ordre de priorité.
Quand on fait Cipher.getInstance("AES/ECB/PKCS5Padding");
, on va chercher le provider le plus prioritaire qui répond à ce besoin. De nombreux providers sont à disposition par défaut mais on peut en rajouter avec la librairie BouncyCastle.
Pour voir les providers disponibles, vous pouvez simplement faire
un
Les providers peuvent être mutualisés dans toute la JVM.
Si vous êtes dans ce cas, que vous avez plusieurs projets sur votre serveur Tomcat et qu’un de ces projet met son provider AES en top priorité dans la liste des providers, il sera en top priorité pour toutes les applications sur votre serveur.
J’ai déjà rencontré un problème où si une application démarrait avant une autre, elle changeait les providers et faisait qu’une application rencontrait des soucis lorsqu’elle voulait chiffrer des éléments.
Sources
https://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html
https://asecuritysite.com/symmetric/padding
Merci pour cet article bien détaillé et clair sur le chiffrement et le déchiffrement en Java. J’apprécie particulièrement l’exemple concret que vous avez fourni, ainsi que les explications détaillées sur les différentes étapes du processus. Je vais certainement utiliser ces informations dans mes propres projets.