Cryptographie en Java. Chiffrement de classe

Bonjour, Habr! Je vous présente la traduction du deuxième article "Java Cipher" de Jakob Jenkov d'une série d'articles pour les débutants qui veulent apprendre les bases de la cryptographie en Java.


Table des matières:


  1. Cryptographie
  2. Chiffre
  3. Messagedigest
  4. Mac
  5. Signature
  6. Keypair
  7. Générateur de clés
  8. KeyPairGenerator
  9. Keystore
  10. Keytool
  11. Attestation
  12. CertificateFactory
  13. CertPath

Java Cipher (Cipher)


La classe Java Cipher ( javax.crypto.Cipher ) est un algorithme de chiffrement. Le terme "Cipher" est un terme standard pour un algorithme de cryptage dans le monde de la cryptographie. C'est pourquoi la classe Java est appelée Cipher , pas Encryptor / Decryptor ou autre chose. Vous pouvez utiliser une instance de chiffrement pour chiffrer et déchiffrer des données en Java. Ce chapitre explique comment fonctionne la classe Cipher.


Création de chiffrement


Avant d'utiliser un chiffrement, vous devez créer une instance de la classe Cipher en appelant sa méthode getInstance () avec un paramètre indiquant le type d'algorithme de chiffrement que vous souhaitez utiliser. Voici un exemple de création d'une instance de chiffrement Java:


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

Cet exemple crée une instance de Cipher à l'aide de l'algorithme de chiffrement AES.


Modes de cryptage


Certains algorithmes de chiffrement peuvent fonctionner dans différents modes. Le mode de cryptage détermine les détails de la façon dont les données seront cryptées. Ainsi, le mode de cryptage affecte partiellement l'algorithme de cryptage. Les modes de cryptage peuvent parfois être utilisés dans plusieurs algorithmes de cryptage différents - comme une méthode qui est ajoutée à l'algorithme de cryptage principal. C'est pourquoi les modes sont considérés séparément des algorithmes de chiffrement eux-mêmes, mais plutôt comme des «ajouts» aux algorithmes de chiffrement. Voici certains des modes de cryptage les plus connus:


  • EBC - Electronic Codebook ( mode Electronic Codebook)
  • CBC - Chaînage de blocs de chiffrement
  • CFB - Cipher Feedback ( mode de chiffrement)
  • OFB - Retour de sortie
  • CTR - Compteur ( mode compteur)

Lors de la création d'une instance d'un chiffrement, vous pouvez ajouter son mode au nom de l'algorithme de chiffrement. Pour créer une instance de chiffrement AES en utilisant le mode de couplage de blocs - Cipher Block Chaining (CBC), vous pouvez procéder comme suit:


 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 

Étant donné que le mode de couplage des blocs de chiffrement nécessite également un «schéma de remplissage», le schéma de remplissage ( PKCS5Padding ) est ajouté à la fin de la chaîne du nom de l'algorithme de chiffrement.
Il est important de savoir que tous les algorithmes et modes de chiffrement ne sont pas pris en charge par défaut par le fournisseur de chiffrement Java SDK. Pour créer l'instance de chiffrement dont vous avez besoin avec le mode et le motif de remplissage requis, vous devrez peut-être installer un fournisseur tiers, tel que Bouncy Castle.


Initialisation du chiffrement


Avant d'utiliser une instance de chiffrement, elle doit être initialisée. Le chiffrement est initialisé en appelant sa méthode init () . La méthode init () prend deux paramètres:


  • Le mode
  • Clé

Un exemple d'initialisation d'une instance de chiffrement en mode chiffrement:


 Key key = ... // /    cipher.init(Cipher.ENCRYPT_MODE, key); 

Et voici un exemple d'initialisation d'une instance de chiffrement déjà en mode de déchiffrement:


 Key key = ... ///    cipher.init(Cipher.DECRYPT_MODE, key); 

Cryptage et décryptage des données


Pour chiffrer ou déchiffrer des données à l'aide d'une instance de chiffrement, l'une de ces deux méthodes est appelée:


  • update()
  • doFinal()

Il existe plusieurs versions remplacées des méthodes update () et doFinal () qui acceptent différents paramètres. Considérez le plus couramment utilisé ici. Si vous devez crypter ou décrypter un bloc de données, appelez simplement doFinal () avec les données pour crypter ou décrypter. Un exemple:


 byte[] plainText = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] cipherText = cipher.doFinal(plainText); 

En fait, le code est approximativement le même dans le cas du déchiffrement des données. N'oubliez pas que l'instance de chiffrement doit être initialisée en mode de déchiffrement. Voici à quoi ressemble le déchiffrement d'un bloc de texte chiffré:


 byte[] plainText = cipher.doFinal(cipherText); 

Si vous devez crypter ou décrypter un gros fichier divisé en plusieurs blocs, update () est appelé une fois pour chaque bloc de données et se termine par un appel à la méthode doFinal () avec le dernier bloc de données. Voici un exemple de chiffrement de plusieurs blocs de données:


 byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8"); byte[] data3 = "01234567890123456789012345".getBytes("UTF-8"); byte[] cipherText1 = cipher.update(data1); byte[] cipherText2 = cipher.update(data2); byte[] cipherText3 = cipher.doFinal(data3); 

La raison pour laquelle l'appel doFinal () est nécessaire pour le dernier bloc de données est que certains algorithmes de chiffrement doivent compléter les données pour s'adapter à une taille de bloc de chiffrement spécifique (par exemple, une limite de 8 octets). Il n'est pas nécessaire de compléter les données chiffrées intermédiaires. Par conséquent, la méthode update () est appelée pour les blocs de données intermédiaires et l'appel doFinal () pour le dernier bloc de données est appelé.
Lorsque vous déchiffrez plusieurs blocs de données, vous appelez également la méthode update () pour les blocs de données intermédiaires et la méthode doFinal () pour le dernier bloc. Exemple de décryptage de plusieurs blocs de données:


 byte[] plainText1 = cipher.update(cipherText1); byte[] plainText2 = cipher.update(cipherText2); byte[] plainText3 = cipher.doFinal(cipherText3); 

Encore une fois, l'instance de chiffrement doit être initialisée en mode de déchiffrement pour que cet exemple fonctionne.


Chiffrement / déchiffrement d'une partie d'un tableau d'octets


Les méthodes de chiffrement et de déchiffrement de la classe Cipher peuvent chiffrer ou déchiffrer certaines des données stockées dans un tableau d'octets. La méthode update () et / ou doFinal () doit transmettre le décalage et la longueur.


 int offset = 10; int length = 24; byte[] cipherText = cipher.doFinal(data, offset, length); 

Dans cet exemple, les octets de l'index 10 et des 24 octets suivants seront chiffrés (ou déchiffrés, selon l'initialisation du chiffrement).


Chiffrement / déchiffrement vers un tableau d'octets existant


Tous les exemples de chiffrement et de déchiffrement de ce chapitre renvoient des données chiffrées ou déchiffrées dans un nouveau tableau d'octets. Cependant, il est également possible de chiffrer ou de déchiffrer des données dans un tableau d'octets existant. Cela peut être utile pour réduire le nombre de tableaux d'octets créés. Pour ce faire, passez le tableau d'octets cible en tant que paramètre à la méthode update () et / ou doFinal () .


 int offset = 10; int length = 24; byte[] dest = new byte[1024]; cipher.doFinal(data, offset, length, dest); 

Dans cet exemple, les données sont chiffrées de 10 index 24 octets vers le tableau d'octets dest avec décalage 0. Si vous souhaitez définir un décalage différent pour le tableau d'octets dest, il existe des versions update () et doFinal () qui acceptent un paramètre de décalage supplémentaire. Un exemple d'appel de la méthode doFinal () avec un décalage dans le tableau dest:


 int offset = 10; int length = 24; byte[] dest = new byte[1024]; int destOffset = 12 cipher.doFinal(data, offset, length, dest, destOffset); 

Réutilisation d'une instance d'un chiffre


L'initialisation d'une instance de chiffrement est une opération coûteuse et la réutilisation des instances de chiffrement est une bonne idée. Heureusement, la classe Cipher a été conçue dans un souci de réutilisabilité. Lorsque vous appelez la méthode doFinal () sur l' instance Cipher, elle revient à l'état dans lequel elle se trouvait immédiatement après l'initialisation. L'instance de chiffrement peut ensuite être utilisée pour chiffrer ou déchiffrer plus de données.


Un exemple de réutilisation d'une instance de chiffrement Java:


 Cipher cipher = Cipher.getInstance("AES"); Key key = ... ///    cipher.init(Cipher.ENCRYPT_MODE, key); byte[] data1 = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); byte[] data2 = "zyxwvutsrqponmlkjihgfedcba".getBytes("UTF-8"); byte[] cipherText1 = cipher.update(data1); byte[] cipherText2 = cipher.doFinal(data2); byte[] data3 = "01234567890123456789012345".getBytes("UTF-8"); byte[] cipherText3 = cipher.doFinal(data3); 

Tout d'abord, une instance de chiffrement est créée et initialisée, puis utilisée pour chiffrer deux blocs de données cohérentes. Notez l'appel à update () puis doFinal () pour ces deux blocs de données. Après cela, l'instance Cipher peut être utilisée à nouveau pour crypter les données. Cela se fait en appelant doFinal () avec un troisième bloc de données. Après cet appel à doFinal (), vous pouvez chiffrer un autre bloc de données avec la même instance de chiffrement Java.

Source: https://habr.com/ru/post/fr444814/


All Articles