web-dev-qa-db-fra.com

javax.crypto.AEADBadTagException: incompatibilité des balises! Erreur lors du cryptage de la chaîne

J'ai écrit une classe auxiliaire d'encryptage et de décryptage simple pour mon Android pour crypter et stocker des chaînes en toute sécurité.

Il consiste en une seule méthode publique statique à chiffrer, puis il appelle une méthode statique privée pour déchiffrer le message chiffré et le renvoie. J'ai écrit la méthode de cette façon pour vérifier si le message est intact après le cryptage/décryptage.

J'ai écrit un test JUnit simple avec une chaîne et appelé AssertEquals sur la chaîne avant et après l'avoir envoyé à la méthode de cryptage Crypto.

J'obtiens les erreurs suivantes en exécutant le test:

javax.crypto.AEADBadTagException: Tag mismatch!

La pile d'erreurs:

at com.Sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.Java:571)
at com.Sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.Java:1046)
at com.Sun.crypto.provider.CipherCore.doFinal(CipherCore.Java:983)
at com.Sun.crypto.provider.CipherCore.doFinal(CipherCore.Java:845)
at com.Sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.Java:446)
at javax.crypto.Cipher.doFinal(Cipher.Java:2165)
at util.Crypto.decrypt(Crypto.Java:94)
at util.Crypto.encrypt(Crypto.Java:64)
at com.example.ALi.meappley.CryptoTest.encryptAndDecryptTest(CryptoTest.Java:29)

Je suis nouveau dans la cryptographie, mais j'ai lu différentes réponses de stackoverflow et je n'ai rien trouvé d'aide. Certains utilisateurs ont suggéré d'appeler cipher.update(someByteArray) avant d'appeler cipher.doFinal(someByteArray) mais je n'ai pas réussi à le faire fonctionner. Aucune suggestion?

Ceci est ma classe d'aide

public class Crypto {

//public methods

//public static encrypt method
public static String encrypt(String messageToEncrypt, @Nullable byte[] associatedData) throws NoSuchPaddingException,
        NoSuchAlgorithmException,
        InvalidAlgorithmParameterException,
        InvalidKeyException,
        BadPaddingException,
        IllegalBlockSizeException {

    byte[] plainBytes = messageToEncrypt.getBytes();
/////////////////////////////////////////////////////////////////
    SecureRandom secureRandom = new SecureRandom();
    byte[] key = new byte[16];
    secureRandom.nextBytes(key);
    SecretKey secretKey = new SecretKeySpec(key, "AES");

    byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
    secureRandom.nextBytes(iv);

    final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

    if (associatedData != null) {
        cipher.updateAAD(associatedData);
    }

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

    ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + cipherText.length);
    byteBuffer.putInt(iv.length);
    byteBuffer.put(iv);
    byteBuffer.put(cipherText);
    byte[] cipherMessage = byteBuffer.array();

    Arrays.fill(key,(byte) 0); //overwrite the content of key with zeros
    ///////////////////////////////////////////////////////////////////

    byte[] decrypted = decrypt(cipherMessage, null, key);

    return decrypted.toString();
}

//public static decrypt method
private static byte[] decrypt(byte[] cipherMessage, @Nullable byte[] associatedData, byte[] key) throws NoSuchPaddingException,
        NoSuchAlgorithmException,
        InvalidAlgorithmParameterException,
        InvalidKeyException,
        BadPaddingException,
        IllegalBlockSizeException {

    ByteBuffer byteBuffer = ByteBuffer.wrap(cipherMessage);
    int ivLength = byteBuffer.getInt();
    if(ivLength < 12 || ivLength >= 16) { // check input parameter
        throw new IllegalArgumentException("invalid iv length");
    }
    byte[] iv = new byte[ivLength];
    byteBuffer.get(iv);
    byte[] cipherText = new byte[byteBuffer.remaining()];
    byteBuffer.get(cipherText);

    final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
    if (associatedData != null) {
        cipher.updateAAD(associatedData);
    }

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

    return plainText;
}
5
Carlton

Il y a quelques problèmes avec votre code:

1) Dans votre méthode de chiffrement, supprimez la ligne suivante (ou placez-la derrière l'appel de déchiffrement).

 Arrays.fill(key, (byte) 0); // overwrite the content of key with zeros

Sinon, la clé de chiffrement et de déchiffrement diffère.

2) Dans votre méthode de chiffrement, passez également les données associées dans votre appel de déchiffrement, c'est-à-dire remplacer

 byte[] decrypted = decrypt(cipherMessage, null, key);

avec

 byte[] decrypted = decrypt(cipherMessage, associatedData, key);

Les données associées transmises pour le chiffrement et le déchiffrement doivent correspondre pour la validité. Pour les besoins des données associées, voir par ex. https://crypto.stackexchange.com/questions/6711/how-to-use-gcm-mode-and-associated-data-properly

3) Dans votre méthode de décryptage, supprimez la ligne

 cipher.update(cipherText);

Pour les besoins de la méthode de mise à jour, voir par ex. Que fait cipher.update en java?

Ces trois problèmes donnent lieu à une exception AEADBadTagException.

4) Je suppose qu'à des fins de test, votre méthode de chiffrement renvoie decrypted.toString () qui ne vous donne cependant que la classe et le code de hachage de l'objet. Il serait plus logique de revenir par exemple nouvelle chaîne (déchiffrée).

3
Topaco