最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

java - Android Kotlin AESGCM Decryption javax.crypto.AEADBadTagException - Stack Overflow

programmeradmin0浏览0评论

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.conscrypt.OpenSSLCipher$EVP_AEAD.throwAEADBadTagExceptionIfAvailable(OpenSSLCipher.java:1216) 
at com.android.conscrypt.OpenSSLCipher$EVP_AEAD.doFinalInternal(OpenSSLCipher.java:1245)
at com.android.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.

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..conscrypt.OpenSSLCipher$EVP_AEAD.throwAEADBadTagExceptionIfAvailable(OpenSSLCipher.java:1216) 
at com.android..conscrypt.OpenSSLCipher$EVP_AEAD.doFinalInternal(OpenSSLCipher.java:1245)
at com.android..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.

Share Improve this question edited Mar 4 at 8:43 Dagon1292 asked Mar 3 at 15:03 Dagon1292Dagon1292 34 bronze badges 4
  • 2 There is no one way for an AES/GCM encryption. You should therefore post the encryption code and/or complete test data (all inputs and outputs of the encryption code) so that the matching decryption code can be found (without guessing). Also note, that the implementation of ModuleDetaxe.getInstance().getEncryptKey()is missing. – Topaco Commented Mar 3 at 16:57
  • 1 The code works when using the default values for AES/GCM (12 bytes IV, 16 bytes tag, concatenation of IV, ciphertext and tag in this order). This is because a Base64 encoded 12 bytes IV is just 16 bytes in size, which corresponds to TAG_LENGTH. However, you must use the Base64 decoding of message.substring(TAG_LENGTH) instead of message regarding messageEncrypted, s. here jdoodle/ia/1DDu. – Topaco Commented Mar 3 at 17:26
  • 1 If the decryption does not work with this change, your data may be corrupted or the encryption logic is implemented differently -> then, as mentioned above: post the encryption code and test data. Note, that the code is easier to understand if a separate parameter is used for the IV length and not TAG_LENGTH. – Topaco Commented Mar 3 at 17:28
  • @Topaco thanks for the help and for giving me a working example. I added my test data to the question. I also used it within your JDoodle and I have another error "Tag mismatch" jdoodle/ia/1DGd – Dagon1292 Commented Mar 4 at 8:43
Add a comment  | 

2 Answers 2

Reset to default 1

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:

  • UTF-8 encode the key
  • Base64 decode the encoded data before IV and ciphertext/tag are separated (use a separate parameter for the IV length, as IV and tag are different things)
  • UTF-8 decode the decoded data
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:

  • For performance and efficiency reasons, a 12 bytes IV should be used if there are no good reasons not to.
  • The key should be Base64 decoded (i.e. AES-128 should be used). If AES-192 or AES-256 is to be applied, a random key of the required size should be applied (and not a 16 bytes key expanded to 24 bytes by Base64 encoding).
  • The IV appears to be UTF-8 encodable, i.e. not a random byte sequence. In this case it is important to ensure that there is no reuse of key/IV pairs as this is a serious vulnerability for CTR based algorithms such as GCM.

The TAG_LENGTH can not be correct: you use it two times for data of different size. Using it's value the first or the second time is most likely wrong:

First you use it to extract the base64 encoded data from the encrypted message: 16 characters decoded with base64 make The base64 decoded raw tag data will be of length 16 / 4 * 3 = 12 bytes.

Second you define the tag length as 16 * 8 = 128 bit. However the extracted IV only has 12 * 8 = 96 bits.

Most likely the first usage of TAG_LENGTH in message.substring(0, TAG_LENGTH) is wrong. If the tag really has 16 raw bytes, then base64 encoded it will have 22 bytes and not 16.

发布评论

评论列表(0)

  1. 暂无评论