如何解决KeyStoreException:使用大于 4072 字节的缓冲区时,Android 8 上出现未知错误
我正在尝试使用 AES/GCM/nopadding
加密(和解密)Android 上的一些敏感文件,256-byte key
保存在 Android Key Store
和 Android 8 上,如果我使用更大的字节缓冲区超过 4072 字节,每次加密都会失败,并出现以下异常
Caused by: javax.crypto.IllegalBlockSizeException
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:491)
at javax.crypto.Cipher.doFinal(Cipher.java:1619)
at javax.crypto.CipherOutputStream.close(CipherOutputStream.java:210)
... 29 more
Caused by: android.security.KeyStoreException: UnkNown error
以下测试代码可用于每次在运行 Android 8 的设备上重现该问题
val ks: KeyStore = KeyStore.getInstance("AndroidKeyStore")
ks.load(null)
val keyGenerator = KeyGenerator.getInstance("AES")
keyGenerator.init(256)
val secretKey = keyGenerator.generateKey()
val keyProtection =
KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
ks.setEntry("keyAlias;",KeyStore.SecretKeyEntry(secretKey),keyProtection)
val key: SecretKey = ks.getKey("keyAlias;",null) as SecretKey
val cipher: Cipher = Cipher.getInstance("AES/GCM/nopadding")
// If i use [secretKey] directly (without reading it from AndroidKeyStore)
// everything works alright everytime for any buffer size
cipher.init(Cipher.ENCRYPT_MODE,key)
// Sample AAD just for demo purposes
cipher.updateAAD(
byteArrayOf(0x87.toByte(),0x11,0xD4.toByte(),0x45,0x2E,0x09,0x0F,0xA6.toByte())
)
val bytesToEncrypt = (0..100000).map { 0x1F.toByte() }.toByteArray()
val inputStream = ByteArrayInputStream(bytesToEncrypt)
val outputStream = bufferedoutputstream(ByteArrayOutputStream())
val cipherOutputStream = CipherOutputStream(outputStream,cipher)
val buffer = ByteArray(4073) // <- Changing this to 4072 makes it work
var byteCount = 0
var bytesReadCount: Int
while (inputStream.read(buffer,buffer.size).also { bytesReadCount = it } != -1) {
byteCount += bytesReadCount
cipherOutputStream.write(buffer,bytesReadCount)
}
cipherOutputStream.flush()
cipherOutputStream.close()
outputStream.close()
inputStream.close()
我凭经验发现任何小于 4073 字节的字节缓冲区大小都可以使用。在任何其他 Android 版本上,这适用于大于 4073 字节的缓冲区。
解密也会发生这种情况,但由于缓冲区是硬编码在 CipherInputStream
内部,因此更难测试,并且需要复制该类(我这样做是为了提高性能,因为库存 512b 缓冲区太小)。>
我知道解决方案是使用较小的缓冲区,但我想知道是否有人知道这可能发生的原因。我真的不明白开发人员控制的缓冲区与 Cipher
实现内部的崩溃之间的联系是什么。
提几点:
- 我试过直接在 Android Key Store 中生成密钥,结果是一样的
- 我已经尝试了 Android 支持的所有 AES// 实现,结果都是一样的
- 我尝试了不同大小的键,结果是一样的
- 正如我在代码注释中提到的,直接使用生成的密钥(在内存中具有其字节的密钥)可以解决问题(不是现实的解决方案)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。