【问题标题】:IllegalBlockSizeException "Invalid input length" when decrypt data解密数据时出现 IllegalBlockSizeException “输入长度无效”
【发布时间】:2020-09-06 10:23:33
【问题描述】:

我正在开发一个安卓应用。

我想将一些敏感数据(jwt 令牌)加密/解密到 SharedPreference 中。

所以我写了下面的代码。

fun initKeyStore() {
    val alias = "${packageName}.rsakeypairs"
    val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }

    if (keyStore.containsAlias(alias)) {

    } else {
        SLog.d(LogTag.SECURE, "[cipher] No keypair for $alias, creating a new one")
        with(KeyPairGenerator.getInstance(KEY_ALGORITHM_RSA, "AndroidKeyStore"), {
            val spec = KeyGenParameterSpec.Builder(alias,
                    PURPOSE_ENCRYPT or PURPOSE_DECRYPT)
                    .setAlgorithmParameterSpec(RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
                    .setBlockModes(BLOCK_MODE_CBC)
                    .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_PKCS1)
                    .setDigests(DIGEST_SHA512, DIGEST_SHA384, DIGEST_SHA256)
                    .setUserAuthenticationRequired(false)
                    .build()
            initialize(spec)
            generateKeyPair()
        })
    }

    keyEntry = keyStore.getEntry(alias, null)
}

fun String.encrypt(): String? {
    cipher.init(Cipher.ENCRYPT_MODE, (keyEntry as KeyStore.PrivateKeyEntry).certificate.publicKey)
    val bytes = this.toByteArray(Charsets.UTF_8)
    val encryptedBytes = cipher.doFinal(bytes)
    val base64EncryptedBytes = Base64.encode(encryptedBytes, Base64.DEFAULT)
    return String(base64EncryptedBytes)
}

fun String.decrypt(): String {
    cipher.init(Cipher.DECRYPT_MODE, (keyEntry as KeyStore.PrivateKeyEntry).privateKey)
    val base64EncryptedBytes = this.toByteArray(Charsets.UTF_8)
    val encryptedBytes = Base64.decode(base64EncryptedBytes, Base64.DEFAULT)
    val decryptedBytes = cipher.doFinal(encryptedBytes)
    return String(decryptedBytes)
}

但是当应用尝试解密加密数据时,发生了异常。

    javax.crypto.IllegalBlockSizeException
        at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:513)
        at javax.crypto.Cipher.doFinal(Cipher.java:2055)
        ...
     Caused by: android.security.KeyStoreException: Invalid input length
        at android.security.KeyStore.getKeyStoreException(KeyStore.java:1539)
        at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)

我使用的 JWT 令牌的长度很长。 (超过 800 个)

如果我尝试加密/解密短文本,它可以正常工作...

如何加密/解密长文本?

【问题讨论】:

  • .setBlockModes(BLOCK_MODE_CBC) 对 RSA 没有意义。我会假设它只是被忽略了,但谁知道呢。此外,加密和解密方向需要自己的 Cipher 实例。

标签: android encryption rsa


【解决方案1】:

为了加密长文本,您要么增加密钥大小(这可能是个坏主意,因为生成此密钥将花费更多时间),或者将文本分成块,加密这些块加一,并将它们保存为字符串数组。

【讨论】:

  • 实际上,我尝试将密钥大小从 2048 -> 8096 增加。但是当它创建密钥对时,会发生异常......所以......我正在考虑你的第二个意见。
  • 这个答案似乎与所报告的异常无关。
【解决方案2】:

非对称加密的最大限制是 245 个字符。

它可以用长字符串的块来修复

   object SecurePreferencesHelper {
    private const val chunkSize = 240

    private fun getNumberOfChunksKey(key: String) = "${key}_numberOfChunks"

    fun setLongStringValue(key: String, value: String) {
        val chunks = value.chunked(chunkSize)

        SecurePreferences.setValue(getNumberOfChunksKey(key), chunks.size)

        chunks.forEachIndexed { index, chunk ->
            SecurePreferences.setValue("$key$index", chunk)
        }
    }

    fun getLongStringValue(key: String): String? {
        val numberOfChunks = SecurePreferences.getIntValue(getNumberOfChunksKey(key), 0)

        if (numberOfChunks == 0) {
            return null
        }

        return (0 until numberOfChunks)
                .map { index ->
                    val string = SecurePreferences.getStringValue("$key$index", null) ?: run {
                        return null
                    }

                    string
                }.reduce { accumulator, chunk -> accumulator + chunk }
    }

    fun removeLongStringValue(key: String) {
        val numberOfChunks = SecurePreferences.getIntValue(getNumberOfChunksKey(key), 0)

        (0 until numberOfChunks).map { SecurePreferences.removeValue("$key$it") }
        SecurePreferences.removeValue(getNumberOfChunksKey(key))
    }

    fun containsLongStringValue(key: String): Boolean {
        return SecurePreferences.contains(getNumberOfChunksKey(key))
    }
}

参考请参考链接

click here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 2020-12-19
    • 1970-01-01
    相关资源
    最近更新 更多