Search code examples
javacryptographyaesbouncycastle

AES/EAX Crypto with JCE - Mac check in EAX failed


I am trying to perform encryption/decryption using AES/EAX/NoPadding. Since EAX doesn't appear to be available without BouncyCastle, BC has been added as a provider.

When I try to encrypt "Hello World!", it appears to have encrypted successfully.

@NotNull
@Override
public byte[] encrypt(@NotNull Key key, @NotNull byte[] plain, @Nullable byte[] authentication) throws CryptoException {
    try {
        final AesEaxKey aesEaxKey = (AesEaxKey) key;
        final Cipher cipher = Cipher.getInstance(getCipherAlgorithm(), BouncyCastleProvider.PROVIDER_NAME);
        final byte[] cipherText = new byte[getIvSize(aesEaxKey) + plain.length + getTagSize()];
        final byte[] iv = randomIv(aesEaxKey);

        System.arraycopy(iv, 0, cipherText, 0, getIvSize(aesEaxKey));
        cipher.init(Cipher.ENCRYPT_MODE, aesEaxKey, getParameterSpec(iv));

        if (authentication != null && authentication.length != 0) {
            cipher.updateAAD(authentication);
        }

        cipher.doFinal(plain, 0, plain.length, cipherText, getIvSize(aesEaxKey));
        return cipherText;
    } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException |
            InvalidKeyException | BadPaddingException | IllegalBlockSizeException | ShortBufferException e) {
        throw new CryptoException(e.getMessage(), e);
    }
}

When I try to decrypt the ciphertext, it throws "Mac check in EAX failed".

@NotNull
@Override
public byte[] decrypt(@NotNull Key key, @NotNull byte[] cipherText, @Nullable byte[] authentication) throws CryptoException {
    try {
        final AesEaxKey aesEaxKey = (AesEaxKey) key;
        final Cipher cipher = Cipher.getInstance(getCipherAlgorithm(), BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(Cipher.DECRYPT_MODE, aesEaxKey, getParameterSpec(cipherText, 0, getIvSize(aesEaxKey)));
        if (authentication != null && authentication.length != 0) {
            cipher.updateAAD(authentication);
        }
        return cipher.doFinal(cipherText, getIvSize(aesEaxKey), cipherText.length - getIvSize(aesEaxKey));
    } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException |
            InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
        throw new CryptoException(e.getMessage(), e);
    }
}

Details:

  1. getParameterSpec() returns an instance of IvParameterSpec with the IV.
  2. During encryption, the IV bytes are inserted into the beginning of the ciphertext byte[] and retrieved from the ciphertext during decryption.
  3. The tag size being used is 16 bytes.
  4. AesEaxKey is simply a wrapper class that implements SecretKey and delegates all of its methods.

I have an implementation of AES/GCM/NoPadding that uses this same exact code which works perfectly.

What am I doing wrong?


Solution

  • AEAD modes such as EAX require a more complicated AlgorithmParameterSpec because both the nonce (aka IV) and the tag length in bits must be specified. Java since 1.7 has provided a GCMParameterSpec for GCM ciphers. The same data is needed for EAX mode, and apparently the Bouncycastle provider will use a GCMParameterSpec for EAX mode as well.

    Note that, for the GCMParameterSpec, the tag length is specified in bits, while for the purposes of sizing arrays the tag length needs to be specified in bytes.