web-dev-qa-db-fra.com

Java AES / GCM / NoPadding - Que me donne cipher.getIV ()?

J'utilise le cryptage AES/GCM/NoPadding Dans Java 8 et je me demande si mon code a un défaut de sécurité. Mon code semble fonctionner , en ce qu'il crypte et décrypte le texte, mais quelques détails ne sont pas clairs.

Ma principale question est la suivante:

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // ?????

Est-ce que l'IV satisfait à l'exigence "Pour une clé donnée, l'IV NE DOIT PAS se répéter." de RFC 4106 ?

J'apprécierais également toutes les réponses/idées pour mes questions connexes (voir ci-dessous), mais cette première question me dérange le plus. Je ne sais pas où trouver le code source ou la documentation qui répond à cela.


Voici en gros le code complet. Je m'excuse au cas où j'aurais introduit des erreurs lors de la rédaction de ce message:

class Encryptor {
  Key key;

  Encryptor(byte[] key) {
    if (key.length != 32) throw new IllegalArgumentException();
    this.key = new SecretKeySpec(key, "AES");
  }

  // the output is sent to users
  byte[] encrypt(byte[] src) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] iv = cipher.getIV(); // See question #1
    assert iv.length == 12; // See question #2
    byte[] cipherText = cipher.doFinal(src);
    assert cipherText.length == src.length + 16; // See question #3
    byte[] message = new byte[12 + src.length + 16]; // See question #4
    System.arraycopy(iv, 0, message, 0, 12);
    System.arraycopy(cipherText, 0, message, 12, cipherText.length);
    return message;
  }

  // the input comes from users
  byte[] decrypt(byte[] message) throws Exception {
    if (message.length < 12 + 16) throw new IllegalArgumentException();
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);
    cipher.init(Cipher.DECRYPT_MODE, key, params);
    return cipher.doFinal(message, 12, message.length - 12);
  }
}

Supposons que les utilisateurs déchiffrent ma clé secrète = game over.


Questions plus détaillées/questions connexes:

  1. Le IV retourné par cipher.getIV () est-il sûr pour moi de l'utiliser de cette façon?

    • Cela évite-t-il la catastrophe de réutiliser l'IV, combinaison de touches en mode Galois/Compteur?
    • Est-il toujours sûr lorsque plusieurs applications exécutent ce code à la fois, affichant toutes des messages chiffrés aux utilisateurs à partir des mêmes données src (éventuellement dans la même milliseconde)?
    • De quoi est fait l'IV retourné? Est-ce un compteur atomique plus un bruit aléatoire?
    • Dois-je éviter cipher.getIV() et construire moi-même un IV, avec mon propre compteur?
    • Le code source implémentant cipher.getIV() est-il disponible en ligne quelque part, en supposant que j'utilise l'extension Oracle JDK 8 + JCE Unlimited Strength?
  2. Cette IV est-elle toujours longue de 12 octets?

  3. La balise d'authentification est-elle toujours de 16 octets (128 bits)?

  4. Avec # 2 et # 3, et le manque de remplissage, cela signifie-t-il que mes messages chiffrés ont toujours 12 + src.length + 16 Octets de long? (Et donc je peux les écraser en toute sécurité dans un tableau d'octets, dont je connais la bonne longueur?)

  5. Puis-je afficher un nombre illimité de cryptages de données src pour les utilisateurs, étant donné les données src constantes que les utilisateurs connaissent?

  6. Est-il sûr pour moi d'afficher un nombre illimité de cryptages de données src pour les utilisateurs, si les données src sont différentes à chaque fois (par exemple, y compris System.currentTimeMillis() ou des nombres aléatoires)?

  7. Serait-il utile que je remplisse les données src avec des nombres aléatoires avant le cryptage? Dites 8 octets aléatoires devant et derrière, ou seulement à une extrémité? Ou est-ce que cela n'aiderait pas du tout/aggraverait mon cryptage?

(Parce que ces questions portent toutes sur le même bloc de mon propre code, et qu'elles sont fortement liées les unes aux autres, et que d'autres pourraient/devraient avoir le même ensemble de questions lors de la mise en œuvre de la même fonctionnalité, il n'était pas judicieux de diviser les questions en plusieurs Je peux les republier séparément si cela est plus approprié pour le format de StackOverflow. Faites le moi savoir!)

32
Michael Hixson

Q1: Le IV retourné par cipher.getIV () est-il sûr pour moi d'utiliser de cette façon?

Oui, c'est au moins pour l'implémentation fournie par Oracle. Il est généré séparément à l'aide de l'implémentation par défaut SecureRandom. Comme sa taille est de 12 octets (la valeur par défaut pour GCM), vous disposez de 96 bits de caractère aléatoire. La chance que le compteur se répète est extrêmement faible. Vous pouvez rechercher la source dans l'OpenJDK (sous GPL) sur lequel le JDK Oracle est basé.

Je vous recommanderais cependant de générer vos propres 12 octets aléatoires car les autres fournisseurs peuvent se comporter différemment.


Q2: Est-ce que cette IV est toujours longue de 12 octets?

C'est extrêmement probable car c'est le GCM par défaut, mais d'autres longueurs sont valides pour GCM. L'algorithme devra cependant effectuer des calculs supplémentaires pour toute autre taille que 12 octets. En raison de faiblesses, il est fortement recommandé de le garder à 12 octets/96 bits et les API peuvent vous limiter à ce choix de taille IV.


Q3: La balise d'authentification est-elle toujours de 16 octets (128 bits)?

Non, il peut avoir n'importe quelle taille en octets allant de 64 bits à 128 bits avec des incréments de 8 bits. S'il est plus petit, il se compose simplement des octets les plus à gauche de la balise d'authentification. Vous pouvez spécifier une autre taille de balise en utilisant GCMParameterSpec comme troisième paramètre pour votre appel init.

Notez que la force de GCM dépend fortement de la taille de l'étiquette. Je recommanderais de le garder à 128 bits. 96 bits devraient être le minimum surtout si vous voulez générer beaucoup de texte chiffré.


Q4: Avec # 2 et # 3, et le manque de remplissage, cela signifie-t-il que mes messages chiffrés ont toujours 12 + src.length + 16 octets de long? (Et donc je peux les écraser en toute sécurité dans un tableau d'octets, dont je connais la bonne longueur?)

Voir au dessus. C'est le cas pour le fournisseur Oracle. Utilisez GCMParameterSpec pour en être sûr.


Q5: Puis-je afficher un nombre illimité de cryptages de données src pour les utilisateurs, étant donné les données src constantes que les utilisateurs connaissent?

Virtuellement non lié, oui. Je commencerais à m'inquiéter après environ 2 ^ 48 cryptages. En général, vous devez cependant concevoir pour changer la clé.


Q6: Puis-je afficher un nombre illimité de cryptages de données src pour les utilisateurs, si les données src sont différentes à chaque fois (par exemple, y compris System.currentTimeMillis () ou des nombres aléatoires)?

Voir la réponse à Q5


Q7: Serait-il utile que je remplisse les données src avec des nombres aléatoires avant le cryptage? Dites 8 octets aléatoires devant et derrière, ou seulement à une extrémité? Ou est-ce que cela n'aiderait pas du tout/aggraverait mon cryptage?

Non, cela n'aiderait pas du tout. GCM utilise le mode CTR en dessous, il serait donc simplement chiffré avec le flux de clés. Ce serait pas agir comme un IV. Si vous avez besoin d'un nombre pratiquement illimité de textes chiffrés (supérieur à 2 ^ 48!), Je vous suggère d'utiliser ce nombre aléatoire et votre clé pour une fonction de dérivation de clé ou KDF. HKDF est actuellement le meilleur de la race, mais vous devrez peut-être utiliser Bouncy Castle ou l'implémenter vous-même.

39
Maarten Bodewes