I wrote a simple Encryption and Decryption helper class for my android app to encrypt and store Strings securely.
It consists of a single static public method to encrypt, then it calls a private static method to decrypt the encrypted message and returns it. I wrote the method this way to check if the message is intact after encryption/decryption.
I wrote a simple JUnit test with a String and called AssertEquals on the String before and after sending it to the Crypto encryption method.
I get this following errors from running the test:
javax.crypto.AEADBadTagException: Tag mismatch!
The error stack:
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)
I'm new to cryptography, but I read different stackoverflow replies and couldn't find anything of help. Some users suggested calling cipher.update(someByteArray)
before calling cipher.doFinal(someByteArray)
but I couldnt manage to get it working. Any suggestions?
This is my helper class
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;
}
There are a few issues with your code:
1) In your encrypt-method remove the following line (or shift it behind the decrypt-call).
Arrays.fill(key, (byte) 0); // overwrite the content of key with zeros
Otherwise the key for encryption and decryption differ.
2) In your encrypt-method also pass the associatedData in your decrypt-call i.e. replace
byte[] decrypted = decrypt(cipherMessage, null, key);
with
byte[] decrypted = decrypt(cipherMessage, associatedData, key);
The associatedData passed for encryption and decryption have to match for validity. For the purpose of the associatedData see e.g. https://crypto.stackexchange.com/questions/6711/how-to-use-gcm-mode-and-associated-data-properly
3) In your decrypt-method remove the line
cipher.update(cipherText);
For the purpose of the update-method see e.g. What does cipher.update do in java?
All three issues give rise to an AEADBadTagException.
4) I suspect for testing purposes your encrypt-method returns decrypted.toString() which however only gives you the object's class and hashcode. It would make more sense to return e.g. new String(decrypted).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With