Chiffrement et décryptage de fichiers en Java

Haut Java

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Vue d'ensemble

Dans ce didacticiel, nous verrons comment crypter et décrypter un fichier à l'aide des API JDK existantes.

2. Écrire d'abord un test

Nous allons commencer par écrire notre test, style TDD. Puisque nous allons travailler avec des fichiers ici, un test d'intégration semble approprié.

Comme nous n'utilisons que la fonctionnalité JDK existante, aucune dépendance externe n'est nécessaire.

Tout d'abord, nous crypterons le contenu à l'aide d'une clé secrète nouvellement générée (nous utilisons AES, Advanced Encryption Standard, comme algorithme de cryptage symétrique dans cet exemple).

Notez également que nous définissons la chaîne de transformation complète dans le constructeur ( AES / CBC / PKCS5Padding ), qui est une concaténation du chiffrement utilisé, du mode de chiffrement par bloc et du remplissage ( algorithme / mode / remplissage ). Les implémentations JDK prennent en charge un certain nombre de transformations différentes par défaut, mais veuillez noter que toutes les combinaisons ne peuvent toujours pas être considérées comme sécurisées cryptographiquement par les normes actuelles.

Nous supposerons que notre classe FileEncrypterDecrypter écrira la sortie dans un fichier appelé baz.enc . Ensuite, nous déchiffrons ce fichier en utilisant la même clé secrète et vérifions que le contenu déchiffré est égal au contenu d'origine:

@Test public void whenEncryptingIntoFile_andDecryptingFileAgain_thenOriginalStringIsReturned() { String originalContent = "foobar"; SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey(); FileEncrypterDecrypter fileEncrypterDecrypter = new FileEncrypterDecrypter(secretKey, "AES/CBC/PKCS5Padding"); fileEncrypterDecrypter.encrypt(originalContent, "baz.enc"); String decryptedContent = fileEncrypterDecrypter.decrypt("baz.enc"); assertThat(decryptedContent, is(originalContent)); new File("baz.enc").delete(); // cleanup }

3. Chiffrement

Nous initialiserons le chiffrement dans le constructeur de notre classe FileEncrypterDecrypter à l'aide de la chaîne de transformation spécifiée .

Cela nous permet d'échouer tôt au cas où une mauvaise transformation serait spécifiée:

FileEncrypterDecrypter(SecretKey secretKey, String transformation) { this.secretKey = secretKey; this.cipher = Cipher.getInstance(transformation); }

Nous pouvons ensuite utiliser le chiffrement instancié et la clé secrète fournie pour effectuer le chiffrement:

void encrypt(String content, String fileName) { cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] iv = cipher.getIV(); try (FileOutputStream fileOut = new FileOutputStream(fileName); CipherOutputStream cipherOut = new CipherOutputStream(fileOut, cipher)) { fileOut.write(iv); cipherOut.write(content.getBytes()); } }

Java nous permet d' exploiter la classe CipherOutputStream pratique pour écrire le contenu chiffré dans un autre OutputStream .

Veuillez noter que nous écrivons le IV (vecteur d'initialisation) au début du fichier de sortie. Dans cet exemple, l'IV est généré automatiquement lors de l'initialisation du chiffrement .

L'utilisation d'un IV est obligatoire lors de l'utilisation du mode CBC, afin de randomiser la sortie cryptée. Le IV n'est cependant pas considéré comme un secret, il est donc normal de l'écrire au début du fichier.

4. Décryptage

Pour déchiffrer, nous devons également lire d'abord le IV. Ensuite, nous pouvons initialiser notre chiffrement et déchiffrer le contenu.

Encore une fois, nous pouvons utiliser une classe Java spéciale, CipherInputStream , qui prend en charge de manière transparente le décryptage réel :

String decrypt(String fileName) { String content; try (FileInputStream fileIn = new FileInputStream(fileName)) { byte[] fileIv = new byte[16]; fileIn.read(fileIv); cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv)); try ( CipherInputStream cipherIn = new CipherInputStream(fileIn, cipher); InputStreamReader inputReader = new InputStreamReader(cipherIn); BufferedReader reader = new BufferedReader(inputReader) ) { StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } content = sb.toString(); } } return content; }

5. Conclusion

Nous avons vu que nous pouvons effectuer un chiffrement et un déchiffrement de base à l'aide de classes JDK standard, telles que Cipher , CipherOutputStream et CipherInputStream .

Comme d'habitude, le code complet de cet article est disponible dans notre référentiel GitHub.

De plus, vous pouvez trouver une liste des chiffrements disponibles dans le JDK ici.

Enfin, notez que les exemples de code ici ne sont pas destinés à être du code de production et que les spécificités de votre système doivent être soigneusement prises en compte lors de leur utilisation.

Fond Java

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS