微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

KeyStoreException:使用大于 4072 字节的缓冲区时,Android 8 上出现未知错误

如何解决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 举报,一经查实,本站将立刻删除。