Dans le développement d'applications web, la sécurisation des données sensibles transitant entre le client et le serveur est primordiale. L'utilisation du standard AES (Advanced Encryption Standard) en mode CBC avec un remplissage PKCS7/PKCS5 offre une solution robuste. Cet article détaille la mise en place d'un mécanisme de chiffrement et de déchiffrement parfaitement interopérable entre un environnement Java et une interface web exploitant la bibliothèque CryptoJS.
Configuration du Backend Java
Pour le côté serveur, l'API standard javax.crypto est employée. Il est crucial de convertir les tableaux d'octets chiffrés en chaînes de caractères via l'encodage Base64. Une tentative de conversion directe en chaîne (par exemple via UTF-8) provoquerait une corruption des données, car les octets générés par l'algorithme ne respectent aucun schéma d'encodage de caractères classique.
package com.security.crypto;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class SymmetricCipherService {
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
// Longueur obligatoire de 16 caractères
private static final String SECRET_PHRASE = "x7G9#kL2pQ4mW8nZ";
private static final String INIT_VECTOR = "aB3cD4eF5gH6iJ7k";
public static String cipherData(String rawText) throws Exception {
byte[] rawBytes = rawText.getBytes(StandardCharsets.UTF_8);
byte[] encryptedBytes = processAes(rawBytes, SECRET_PHRASE, INIT_VECTOR, Cipher.ENCRYPT_MODE);
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decipherData(String encodedText) throws Exception {
byte[] decodedBytes = Base64.getDecoder().decode(encodedText);
byte[] decryptedBytes = processAes(decodedBytes, SECRET_PHRASE, INIT_VECTOR, Cipher.DECRYPT_MODE);
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
private static byte[] processAes(byte[] input, String keyStr, String ivStr, int mode) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(keyStr.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivStr.getBytes(StandardCharsets.UTF_8));
Cipher aesCipher = Cipher.getInstance(ALGORITHM);
aesCipher.init(mode, secretKey, ivSpec);
return aesCipher.doFinal(input);
}
public static void main(String[] args) {
try {
String originalMessage = "Données confidentielles 2023";
System.out.println("Original: " + originalMessage);
String securedMessage = cipherData(originalMessage);
System.out.println("Chiffré (Base64): " + securedMessage);
String restoredMessage = decipherData(securedMessage);
System.out.println("Déchiffré: " + restoredMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Configuration du Frontend avec CryptoJS
Sur le navigateur, la bibliothèque CryptoJS permet de reproduire exactement la même logique cryptographique. Il est impératif de spécifier explicitement le mode CBC et le schéma de remplissage Pkcs7 pour garantir la compatibilité avec l'implémentation Java.
const SECRET_PHRASE = 'x7G9#kL2pQ4mW8nZ';
const INIT_VECTOR = 'aB3cD4eF5gH6iJ7k';
const cipherPayload = (rawData, secret, vector) => {
const parsedKey = CryptoJS.enc.Utf8.parse(secret);
const parsedIv = CryptoJS.enc.Utf8.parse(vector);
const cipherResult = CryptoJS.AES.encrypt(rawData, parsedKey, {
iv: parsedIv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// Retourne nativement une chaîne au format Base64
return cipherResult.toString();
};
const decipherPayload = (encodedData, secret, vector) => {
const parsedKey = CryptoJS.enc.Utf8.parse(secret);
const parsedIv = CryptoJS.enc.Utf8.parse(vector);
const decipherResult = CryptoJS.AES.decrypt(encodedData, parsedKey, {
iv: parsedIv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return decipherResult.toString(CryptoJS.enc.Utf8);
};
// Exécution du test de validation
const executeTest = () => {
const initialText = "Données confidentielles 2023";
console.log(`[Client] Original: ${initialText}`);
const securedText = cipherPayload(initialText, SECRET_PHRASE, INIT_VECTOR);
console.log(`[Client] Chiffré: ${securedText}`);
const restoredText = decipherPayload(securedText, SECRET_PHRASE, INIT_VECTOR);
console.log(`[Client] Déchiffré: ${restoredText}`);
};
executeTest();
Contraintes et Résolution des Anomalies
- Taille des paramètres cryptographiques : La clé secrète et le vecteur d'initialisation (IV) doivent impérativement mesurer 16 octets (128 bits). Toute divergence de longueur ou de valeur entre le client et le serveur entraînera des échecs de déchiffrement.
- Encodage de transport : Les algorithmes comme AES transforment les données en séquences binaires arbitraires. L'utilisation du Base64 est obligatoire pour transporter cette séquence sous forme textuelle via des protocoles comme HTTP sans subir d'altération ou de troncature.
- Anomalie de taille de bloc : L'exception Java
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded ciphersurvient lorsque le texte chiffré a été corrompu durant le transit. Cela indique généralement que le décodage Base64 n'a pas été correctement appliqué avant de soumettre les octets au moteur de déchiffrement.