Android KeyStore RSA解密抛出Invalid input length异常排查
问题:Android KeyStore RSA解密抛出Invalid input length异常
开发Android应用时基于Android KeyStore实现RSA加解密,密钥生成、加密流程运行正常,解密环节抛出android.security.KeyStoreException: Invalid input length异常,耗时两天排查各类方案未解决。
现有实现代码
密钥生成代码
private var provider = fun generateKeys(): KeyPair { val keyStore = KeyStore.getInstance(provider) val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, provider); val parmeterSpec = KeyGenParameterSpec.Builder( ALIAS, KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT ).run { setUserAuthenticationRequired(false) setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) // 参考帖子建议添加,用于解决历史问题 setRandomizedEncryptionRequired(true) build() } kpg.initialize(parmeterSpec) val kp = kpg.generateKeyPair() return kp }
加密代码
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" fun encrypt(message: String, publicKey: PublicKey) : String? { // 参考帖子建议MGF使用SHA1 val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT) val cipher = Cipher.getInstance(method) cipher.init(Cipher.ENCRYPT_MODE, publicKey) val encryptedBytes = cipher.doFinal(message.toByteArray()) return Base64.encodeToString(encryptedBytes, Base64.DEFAULT) }
解密代码
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" fun decrypt(encrypted: String, privateKey: PrivateKey) : String? { val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT) val cipher = Cipher.getInstance(method) cipher.init(Cipher.DECRYPT_MODE, privateKey, spec) val decryptedBytes = cipher.doFinal(encrypted.toByteArray()) return Base64.encodeToString(decryptedBytes, Base64.DEFAULT) }
调用逻辑
val keyPair = generateKeys() val message = "a simple message" val encryptedMessage = encrypt(message, keyPair.public) val result = decrypt(encryptedMessage, keyPair.private)
已尝试排查方案
- 验证密文长度:RSA 2048位密钥最大支持256字节输入,当前加密后密文长度仅175字节,不存在超长问题
- 处理Base64换行:解密前替换密文字符串中的
\n字符,未解决问题 - 参考多篇同类问题技术帖调整MGF摘要参数、KeyStore配置,均未生效
异常堆栈
Caused by: javax.crypto.IllegalBlockSizeException at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:513) at javax.crypto.Cipher.doFinal(Cipher.java:2055) at com.sandwich.testkeystore.MainActivity.decrypt(MainActivity.kt:132) at com.sandwich.testkeystore.MainActivity.onCreate(MainActivity.kt:65) at android.app.Activity.performCreate(Activity.java:7822) at android.app.Activity.performCreate(Activity.java:7811) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1328) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3452) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3620) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2183) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:241) at android.app.ActivityThread.main(ActivityThread.java:7604) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941) Caused by: android.security.KeyStoreException: Invalid input length at android.security.KeyStore.getKeyStoreException(KeyStore.java:1303) at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132) at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:217) at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506) at javax.crypto.Cipher.doFinal(Cipher.java:2055) at com.sandwich.testkeystore.MainActivity.decrypt(MainActivity.kt:132) at com.sandwich.testkeystore.MainActivity.onCreate(MainActivity.kt:65) at android.app.Activity.performCreate(Activity.java:7822) at android.app.Activity.performCreate(Activity.java:7811) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1328) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3452) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3620) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2183) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:241) at android.app.ActivityThread.main(ActivityThread.java:7604) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)
解决方案
代码存在3个核心问题,按顺序修复即可:
- Base64处理逻辑完全错误
加密返回的是密文字节经Base64编码后的字符串,解密时需要先对该字符串做Base64解码得到原始密文字节,再传入Cipher解密。当前代码直接调用encrypted.toByteArray()拿到的是Base64字符串本身的字节数组,长度和原始密文不一致,直接触发Invalid input length异常。
同时解密得到的是明文字节,直接转UTF-8字符串即可,不需要再做Base64编码。 - 加密初始化未传入OAEP参数,加解密参数不匹配
加密函数中定义了OAEPParameterSpec但初始化Cipher时未传入,会导致加密时使用默认MGF参数,和解密时传入的自定义参数不一致,即使修复Base64问题也会抛出填充错误。 - KeyStore provider未赋值
密钥生成代码中provider变量为空,需要固定赋值为"AndroidKeyStore",否则会调用系统默认加密提供者,无法使用KeyStore的安全能力。
修复后代码
密钥生成代码补全
// 固定指定AndroidKeyStore提供者 private var provider = "AndroidKeyStore" private const val ALIAS = "your_key_alias" fun generateKeys(): KeyPair { val keyStore = KeyStore.getInstance(provider) val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, provider) val parameterSpec = KeyGenParameterSpec.Builder( ALIAS, KeyProperties.PURPOSE_DECRYPT or KeyProperties.PURPOSE_ENCRYPT ).run { setUserAuthenticationRequired(false) setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP) setRandomizedEncryptionRequired(true) build() } kpg.initialize(parameterSpec) return kpg.generateKeyPair() }
修复后加密函数
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" fun encrypt(message: String, publicKey: PublicKey) : String? { val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT) val cipher = Cipher.getInstance(method) // 加密时传入OAEP参数,保证加解密参数一致 cipher.init(Cipher.ENCRYPT_MODE, publicKey, spec) val encryptedBytes = cipher.doFinal(message.toByteArray(Charsets.UTF_8)) return Base64.encodeToString(encryptedBytes, Base64.DEFAULT) }
修复后解密函数
private var method = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding" fun decrypt(encrypted: String, privateKey: PrivateKey) : String? { val spec = OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT) val cipher = Cipher.getInstance(method) cipher.init(Cipher.DECRYPT_MODE, privateKey, spec) // 先Base64解码得到原始密文字节 val encryptedBytes = Base64.decode(encrypted, Base64.DEFAULT) val decryptedBytes = cipher.doFinal(encryptedBytes) // 明文字节直接转字符串,无需二次Base64编码 return String(decryptedBytes, Charsets.UTF_8) }
额外注意事项
- RSA加密有长度限制,若需加密超过190字节(OAEP填充下2048位密钥的最大明文长度)的内容,不要直接用RSA加密明文,需采用混合加密方案:生成随机AES密钥加密明文,再用RSA公钥加密AES密钥,解密时先RSA解密得到AES密钥,再用AES解密密文。
- 字符串转字节数组时统一指定
Charsets.UTF_8字符集,避免不同系统默认字符集不一致导致的乱码问题。
内容的提问来源于stack exchange,提问作者Horia Pantelimon
相关产品推荐
相关产品推荐

