【发布时间】:2025-11-30 19:10:01
【问题描述】:
我正在尝试编写几个函数,这些函数可以使用 AES/GCM 加密和 PBKDF2 密钥生成来加密和解密文本。我正在从 CTR (SIC) 加密转换我的代码,而当所有其他方法都正常工作时,MAC 检查失败正在杀死我。
fun encryptAESBasic(input: String, password: String): String {
val masterpw = getKey(password)
val random = SecureRandom()
val salt = ByteArray(16)
random.nextBytes(salt)
val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec: KeySpec = PBEKeySpec(masterpw.toString().toCharArray(), salt, 100, 256)
val tmp: SecretKey = factory.generateSecret(spec)
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
cipher.init(Cipher.ENCRYPT_MODE, tmp, IvParameterSpec(iv))
val cipherText: ByteArray = cipher.doFinal(input.toByteArray(Charset.forName("UTF-8")))
val ivstring: String = Base64.encodeToString(iv, Base64.NO_WRAP)
val saltystring: String = Base64.encodeToString(salt, Base64.NO_WRAP)
val cipherstring: String = Base64.encodeToString(cipherText, Base64.NO_WRAP)
val returnstring: String = ivstring + "-" + saltystring + "-" + cipherstring
return returnstring
}
fun decryptAESBasic(text: String, password: String): String {
val arr = text.split("-")
val iv = Base64.decode(arr[0].toByteArray(Charset.forName("UTF-8")), Base64.NO_WRAP)
val salt = Base64.decode(arr[1].toByteArray(Charset.forName("UTF-8")), Base64.NO_WRAP)
val data = arr[2].toByteArray(Charset.forName("UTF-8"))
val masterpw = getKey(password)
val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec: KeySpec = PBEKeySpec(masterpw.toString().toCharArray(), salt, 100, 256)
val tmp: SecretKey = factory.generateSecret(spec)
val key: ByteArray = tmp.getEncoded()
val cipher = Cipher.getInstance("AES/CTR/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, tmp, IvParameterSpec(iv))
val credential: ByteArray = cipher.doFinal(Base64.decode(data, Base64.NO_WRAP))
return credential.toString(Charset.forName("UTF-8"))
}
fun getKey(masterPass: String): ByteArray {
return masterPass.padEnd(32, '.').toByteArray(Charset.forName("UTF-8"))
}
同样,此代码有效,但我想将其从 CTR 更改为 GCM,但每次我这样做时都会遇到“mac check in GCM failed”错误。任何帮助解释如何/为什么会发生这种情况将不胜感激。
E/AndroidRuntime( 6461): Caused by: javax.crypto.AEADBadTagException: mac check in GCM failed
E/AndroidRuntime( 6461): at java.lang.reflect.Constructor.newInstance0(Native Method)
E/AndroidRuntime( 6461): at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
E/AndroidRuntime( 6461): at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(BaseBlockCipher.java:1485)
E/AndroidRuntime( 6461): at com.android.org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(BaseBlockCipher.java:1217)
E/AndroidRuntime( 6461): at javax.crypto.Cipher.doFinal(Cipher.java:2055)
E/AndroidRuntime( 6461): at design.codeux.autofill_service.FlutterMyAutofillServiceKt.decryptAESBasic(FlutterMyAutofillService.kt:1003)
【问题讨论】:
-
尝试在解码密码时指定编码,在加密和解密过程中:
masterpw.toString(Charset.forName("UTF-8")。如果没有这个规范,CTR 和 GCM 都不能在我的机器上运行。根据规范,CTR 和 GCM 都可以工作。不同的行为可能是由于不同的默认编码。顺便说一句,GCM 使用 12 字节的 nonce/IV。 -
此外,通常组件(盐、IV、密文)在字节级别连接,结果是 Base64 编码的。不需要分隔符,因为盐和 IV 的长度在两边都是已知的。但这更像是一种约定,而不是必须的。
-
非常感谢!您对另一个加密问题的回答实际上是促使我从 CTR 更改为 GCM 的原因
-
为了完整性:
ByteArray.toString(charset: Charset)使用指定的字符集执行解码,而ByteArray.toString()像在 Java 中一样返回 object's classname@hashcode(例如 [B@481e504)。
标签: kotlin encryption cryptography pbkdf2 aes-gcm