Guide de la classe de chiffrement

1. Vue d'ensemble

En termes simples, le cryptage est le processus de codage d'un message de sorte que seuls les utilisateurs autorisés puissent le comprendre ou y accéder.

Le message, appelé texte brut , est crypté à l' aide d' un algorithme de chiffrement - un chiffrement - génération cryptogramme qui ne peut être lu par les utilisateurs autorisés via le décryptage.

Dans cet article, nous décrivons en détail la classe de chiffrement principale , qui fournit des fonctionnalités de chiffrement et de déchiffrement cryptographiques en Java.

2. Classe de chiffrement

Java Cryptography Extension (JCE) est la partie de Java Cryptography Architecture (JCA) qui fournit une application avec des chiffrements cryptographiques pour le chiffrement et le déchiffrement des données ainsi que le hachage de données privées.

La classe Cipher - située dans le package javax.crypto - forme le cœur du framework JCE, fournissant les fonctionnalités de cryptage et de décryptage.

2.1. Instanciation de chiffrement

Pour instancier un objet Cipher , nous appelons la méthode statique getInstance , en passant le nom de la transformation demandée . Facultativement, le nom d'un fournisseur peut être spécifié.

Écrivons un exemple de classe illustrant l'instanciation d'un Cipher :

public class Encryptor { public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); //... } }

La transformation AES / ECB / PKCS5Padding indique à la méthode getInstance d'instancier l' objet Cipher en tant que chiffrement AES avec le mode de fonctionnement ECB et le schéma de remplissage PKCS5.

Nous pouvons également instancier l' objet Cipher en spécifiant uniquement l'algorithme dans la transformation:

Cipher cipher = Cipher.getInstance("AES");

Dans ce cas, Java utilisera les valeurs par défaut spécifiques au fournisseur pour le mode et le schéma de remplissage.

Notez que getInstance lèvera une NoSuchAlgorithmException si la transformation est nulle , vide ou dans un format non valide, ou si le fournisseur ne la prend pas en charge.

Il lèvera une NoSuchPaddingException si la transformation contient un schéma de remplissage non pris en charge.

2.2. Fil-Sécurité

La classe Cipher est une classe avec état sans aucune forme de synchronisation interne. En fait, des méthodes comme init () ou update () changeront l'état interne d'une instance Cipher particulière .

Par conséquent, la classe Cipher n'est pas thread-safe. Nous devons donc créer une Cipher instance par besoin de chiffrement / déchiffrement.

2.3. Clés

L' interface Key représente les clés pour les opérations cryptographiques. Les clés sont des conteneurs opaques qui contiennent une clé codée, le format de codage de la clé et son algorithme cryptographique.

Les clés sont généralement obtenues via des générateurs de clés, des certificats ou des spécifications de clés à l'aide d'une fabrique de clés.

Créons une clé symétrique à partir des octets de clé fournis:

SecretKey secretKey = new SecretKeySpec(keyBytes, "AES");

2.4. Initialisation du chiffrement

Nous appelons la méthode init () pour initialiser l' objet C ipher avec une clé ou un certificat et un opmode indiquant le mode de fonctionnement du chiffrement.

En option, on peut passer dans une source d'aléatoire . Par défaut, une implémentation SecureRandom du fournisseur installé ayant la priorité la plus élevée est utilisée. Sinon, il utilisera une source fournie par le système.

Nous pouvons éventuellement spécifier un ensemble de paramètres spécifiques à l'algorithme. Par exemple, nous pouvons passer un IvParameterSpec pour spécifier un vecteur d'initialisation .

Voici les modes de fonctionnement de chiffrement disponibles:

  • ENCRYPT_MODE : initialize chiffrement objet en mode de chiffrement
  • DECRYPT_MODE : initialise l' objet de chiffrement en mode de déchiffrement
  • WRAP_MODE : initialise l' objet de chiffrement en mode d' encapsulation de clé
  • UNWRAP_MODE : initialise l' objet de chiffrement en mode de déballage de clé

Initialisons l' objet Cipher :

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); // ...

Désormais, la méthode init lève une exception InvalidKeyException si la clé fournie n'est pas appropriée pour initialiser le chiffrement, comme lorsqu'une longueur / un codage de clé n'est pas valide.

It's also thrown when the cipher requires certain algorithm parameters that cannot be determined from the key, or if the key has a key size that exceeds the maximum allowable key size (determined from the configured JCE jurisdiction policy files).

Let's look at an example using a Certificate:

public byte[] encryptMessage(byte[] message, Certificate certificate) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, certificate); // ... }

The Cipher object gets the public key for data encryption from the certificate by calling the getPublicKey method.

2.5. Encryption and Decryption

After initializing the Cipher object, we call the doFinal() method to perform the encryption or decryption operation. This method returns a byte array containing the encrypted or decrypted message.

The doFinal() method also resets the Cipher object to the state it was in when previously initialized via a call to init() method, making the Cipher object available to encrypt or decrypt additional messages.

Let's call doFinal in our encryptMessage method:

public byte[] encryptMessage(byte[] message, byte[] keyBytes) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(message); }

To perform a decrypt operation, we change the opmode to DECRYPT_MODE:

public byte[] decryptMessage(byte[] encryptedMessage, byte[] keyBytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(keyBytes, "AES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(encryptedMessage); }

2.6. Providers

Designed to use a provider-based architecture, the JCE allows for qualified cryptography libraries such as BouncyCastle to be plugged in as security providers and new algorithms to be added seamlessly.

Now let's add BouncyCastle as a security provider. We can add a security provider either statically or dynamically.

To add BouncyCastle statically, we modify the java.security file located in /jre/lib/security folder.

We add the line at the end of the list:

... security.provider.4=com.sun.net.ssl.internal.ssl.Provider security.provider.5=com.sun.crypto.provider.SunJCE security.provider.6=sun.security.jgss.SunProvider security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider

When adding the provider property, the property key is in the format security.provider.N where the number N is one more than the last one on the list.

We can also add the BouncyCastle security provider dynamically without having to modify the security file:

Security.addProvider(new BouncyCastleProvider());

We can now specify the provider during cipher initialization:

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");

BC specifies BouncyCastle as the provider. We can get the list of registered providers via the Security.getProviders() method.

3. Testing Encryption and Decryption

Let's write an example test to illustrate message encryption and decryption.

In this test, we use AES encryption algorithm with a 128-bit key and assert that the decrypted result is equal to the original message text:

@Test public void whenIsEncryptedAndDecrypted_thenDecryptedEqualsOriginal() throws Exception { String encryptionKeyString = "thisisa128bitkey"; String originalMessage = "This is a secret message"; byte[] encryptionKeyBytes = encryptionKeyString.getBytes(); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); SecretKey secretKey = new SecretKeySpec(encryptionKeyBytes, "AES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] encryptedMessageBytes = cipher.doFinal(message.getBytes()); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] decryptedMessageBytes = cipher.doFinal(encryptedMessageBytes); assertThat(originalMessage).isEqualTo(new String(decryptedMessageBytes)); }

4. Conclusion

In this article, we discussed the Cipher class and presented usage examples. More details on the Cipher class and the JCE Framework can be found in the class documentation and the Java Cryptography Architecture (JCA) Reference Guide.

La mise en œuvre de tous ces exemples et extraits de code peut être trouvée sur GitHub . Il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.