I'm trying to decrypt a string encoded in AES with GCM mode by another application.
When I pass my test string to my decrypt method, I get this fatal execption :
javax.crypto.AEADBadTagException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at com.android.org.conscrypt.OpenSSLCipher$EVP_AEAD.throwAEADBadTagExceptionIfAvailable(OpenSSLCipher.java:1216)
at com.android.org.conscrypt.OpenSSLCipher$EVP_AEAD.doFinalInternal(OpenSSLCipher.java:1245)
at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:363)
at javax.crypto.Cipher.doFinal(Cipher.java:2055)
at fr.lundimatin.core.utils.AESEncryptionUtil$Companion.decrypt(AESEncryptionUtil.kt:40)
I'm using one method to convert the encryption key string I have to a SecretKey object and the other to decrypt a test string, see my code :
const val IV_LENGTH = 16
fun getSecretKey(key: String) : SecretKey
{
val decodedKey: ByteArray = Base64.getDecoder().decode(key)
val originalKey: SecretKey = SecretKeySpec(decodedKey, 0, decodedKey.size, "AES")
return originalKey
}
//message : "MjY2NjI4QkI0NDYwRTExMxPQoQrBDTPMWTTIAhJ5QH4cKjzmWIIfW0mBv3zIC7yHJr5nqfmmEYk34BW9ag=="
fun decrypt(message: String): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val ivString = message.substring(0, IV_LENGTH)
val ivEncrypted = Base64.getDecoder().decode(ivString)
val spec = GCMParameterSpec(128, ivEncrypted)
cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(), spec)
val messageEncrypted = Base64.getDecoder().decode(message.substring(IV_LENGTH))
val decryptedBytes = cipher.doFinal(messageEncrypted)
return String(decryptedBytes, StandardCharsets.UTF_8) // 400172870-327468066356882-CHE
}
fun getEncryptionKey() : SecretKey {
return getSecretKey("k8h1k9LyiHgcStf/crHlkw==")
}
Unfortunately the message is not very helpful and I havent found a solution on StackOverflow that would apply to my case.
Edit : added my test data within the code.
The length of the encrypted data and the absence of an explicit IV and tag are an indication that a concatenation of IV, ciphertext and tag is most likely used (assuming the usual order IV|ciphertext|tag
, which is just a guess).
Based on the plaintext length of 29 bytes (which also means a ciphertext length of 29 bytes), it can be concluded that a 16 bytes IV (and not the IV length of 12 bytes recommended for GCM) and a 16 bytes tag were used:
Base64 MjY2NjI4QkI0NDYwRTExMxPQoQrBDTPMWTTIAhJ5QH4cKjzmWIIfW0mBv3zIC7yHJr5nqfmmEYk34BW9ag==
IV ciphertext tag
Hex 32363636323842423434363045313133 13d0a10ac10d33cc5934c8021279407e1c2a3ce658821f5b4981bf7cc8 0bbc8726be67a9f9a6118937e015bd6a
Nevertheless, decryption with this data fails. Since the encryption code is not known, one can only try to guess possible reasons for this.
One possibility is that the key does not have to be Base64 decoded, but UTF-8 encoded (i.e. AES-192 is used instead of AES-128).
With this change, the decryption is successful, as can be seen here on CyberChef.
Now that the encryption logic has been deduced with the help of the test data, the code can be adapted:
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import javax.crypto.SecretKey
import java.util.Base64
import java.nio.charset.StandardCharsets
fun main() {
println(decrypt("MjY2NjI4QkI0NDYwRTExMxPQoQrBDTPMWTTIAhJ5QH4cKjzmWIIfW0mBv3zIC7yHJr5nqfmmEYk34BW9ag=="))
}
const val TAG_LENGTH = 16
const val IV_LENGTH = 16
fun getSecretKey(key: String) : SecretKey {
val decodedKey: ByteArray = key.toByteArray(StandardCharsets.UTF_8) // Fix 1: UTF-8 encode key
val originalKey: SecretKey = SecretKeySpec(decodedKey, 0, decodedKey.size, "AES")
return originalKey
}
fun decrypt(messageB64: String): String {
val message = Base64.getDecoder().decode(messageB64) // Fix 2: Base64 decode before separating IV and ciphertext/tag
val iv = message.take(IV_LENGTH).toByteArray()
val ciphertext = message.drop(IV_LENGTH).toByteArray()
val spec = GCMParameterSpec(TAG_LENGTH * 8, iv)
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(), spec)
val decryptedBytes = cipher.doFinal(ciphertext)
return String(decryptedBytes, StandardCharsets.UTF_8) // Fix 3: UTF-8 decode data
}
fun getEncryptionKey() : SecretKey {
return getSecretKey("k8h1k9LyiHgcStf/crHlkw==")
}
Security: Although the encryption code is not known, there are hints of vulnerabilities/inefficiencies: