java - 在 Android 中解密使用 AES/GCM/NoPadding 加密的消息时出错
问题描述
我目前正在使用 AES/GCM/NoPadding 来执行密码操作。
我的加密代码:
fun encrypt(plainText: ByteArray, key: Key): ByteArray? {
var resultText: ByteArray? = null
try {
val cipher = Cipher.getInstance(ALGORITHM)
cipher.init(Cipher.ENCRYPT_MODE, key)
val cipherText = cipher.doFinal(plainText)
resultText = ByteBuffer.allocate(1 + cipher.iv.size + cipherText.size)
.put(cipher.iv.size.toByte())
.put(cipher.iv)
.put(cipherText)
.array()
} catch (e : Exception) {
Logger.e(TAG, "Error encrypting plain text", e)
}
return resultText
}
我的解密代码:
fun decrypt(cipherTextWithHeaders: ByteArray, key: Key): ByteArray? {
var plainText: ByteArray? = null
try {
val cipher = Cipher.getInstance(ALGORITHM)
val ivSize = cipherTextWithHeaders[0].toInt()
val iv = ByteArray(ivSize)
System.arraycopy(cipherTextWithHeaders, 1, iv, 0, ivSize)
cipher.init(Cipher.DECRYPT_MODE, key, GCMParameterSpec(ivSize * 8, iv))
val headerLen = 1 + ivSize
val cipherText = ByteArray(cipherTextWithHeaders.size - headerLen)
System.arraycopy(cipherTextWithHeaders, headerLen, cipherText, 0, cipherTextWithHeaders.size - headerLen)
plainText = cipher.doFinal(cipherText)
} catch (e : Exception) {
Logger.e(TAG, "Error decrypting cipher text", e)
}
return plainText
}
在上面的解密方法中执行 doFinal 时出现此异常:
javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
at javax.crypto.Cipher.doFinal(Cipher.java:1736)
我在加密期间尝试了以下选项:
val temp = ByteArray(12)
SecureRandom().nextBytes(temp)
cipher.init(Cipher.ENCRYPT_MODE, key, GCMParameterSpec(96, temp))
但这会引发以下错误:
java.security.InvalidAlgorithmParameterException: Caller-provided IV not permitted
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:85)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:265)
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:148)
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:2659)
at javax.crypto.Cipher.tryCombinations(Cipher.java:2570)
at javax.crypto.Cipher$SpiAndProviderUpdater.updateAndGetSpiAndProvider(Cipher.java:2475)
at javax.crypto.Cipher.chooseProvider(Cipher.java:566)
at javax.crypto.Cipher.init(Cipher.java:973)
at javax.crypto.Cipher.init(Cipher.java:908)
解决方案
GCM auth 标签长度与 IV 长度无关。AES-GCM 的标准实际上是12
-bytes IV 和128
bits GCM 标签,参见RFC 5288, Section 3。
例子:
String input = "abcdef";
byte[] key = new byte[16];
(new SecureRandom()).nextBytes(key);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
byte[] ciphertext = cipher.doFinal(input.getBytes());
byte[] iv = cipher.getIV();
GCMParameterSpec gcmspec = cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
System.out.println("ciphertext: " + ciphertext.length + ", IV: " + iv.length + ", tLen: " + gcmspec.getTLen());
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
byte[] plaintext = cipher.doFinal(ciphertext);
System.out.println("plaintext : " + new String(plaintext));
印刷:
ciphertext: 22, IV: 12, tLen: 128
plaintext : abcdef
尝试更改GCMParameterSpec(ivSize * 8, iv)
为GCMParameterSpec(128, iv)
.
尽管问题也可能在外部,即密文可能在某处被错误编码或截断。检查cipherText.length
。
java.security.InvalidAlgorithmParameterException: Caller-provided IV not permitted
这是 Android 加密实现的限制;它想在加密期间自己生成 IV。
推荐阅读
- spring - 具有嵌套属性的 Spring REST 请求(在树结构中)
- reactjs - 使用 react-bootstrap 时,只能在函数组件的主体内部调用钩子
- string - 用于格式化日期和时间的 Dart 函数
- ssh - mdt shell 未连接或推送 ssh 密钥
- angular - Angular 中的链式 Observable RxJ
- python - 从python中删除sql表行
- javascript - 如何在 $match 聚合 mongodb 上分配动态键?
- c# - Twilio API - 发送短信时请求过多
- amazon-web-services - Express/AwsServerlessExpress/lambda/Secrets Manager - 从函数中导出变量 -
- ios - 是否可以在 SwiftUI 2.0 中使用 OpenURLAction 拨打电话?