Android Kotlin RSA 1024公钥解密失败,适配Delphi协议遇阻
问题分析与解决方案
你的核心问题是在Android Kotlin端实现RSA私钥加密内容的公钥解密,这对应RSA的签名验证场景,但由于TurboPower LockBox2(Delphi端)的非标准实现、密钥格式问题,以及不同Padding方案的支持限制,导致解密失败。以下是针对性的解决步骤:
关键限制说明
- OAEP Padding的限制:OAEP是标准RSA加密的Padding方案,严格要求「公钥加密、私钥解密」,不支持私钥加密后用公钥解密,因此你尝试的两种OAEP模式必然报错。
- PKCS#1 v1.5 Padding的兼容性:PKCS#1 v1.5同时支持两种场景:
- 加密:公钥加密 → 私钥解密
- 签名:私钥加密(签名) → 公钥解密(验证)
你之前用RSA/ECB/PKCS1Padding报错,是因为Delphi端LockBox2的Padding实现与标准Java Provider不匹配,或密钥不符合RSA规范。
- 密钥规范问题:你补充的错误(公钥指数偶数、模数偶数、小质因子),说明LockBox2生成的密钥不符合标准RSA要求——标准RSA要求模数是两个大质数的乘积(必须为奇数),公钥指数
e必须是奇数(通常用65537即0x10001)。
解决方案步骤
1. 修复密钥规范
放弃使用LockBox2生成密钥,改用标准工具生成符合PKCS#1规范的1024位RSA密钥对:
# 生成PKCS#1格式私钥(PEM) openssl genrsa -out private.pem 1024 # 导出PKCS#1格式公钥(PEM) openssl rsa -in private.pem -pubout -out public.pem -RSAPublicKey_out
将PEM文件中的Base64部分(去掉-----BEGIN RSA PUBLIC KEY-----和-----END RSA PUBLIC KEY-----标记)导入到Delphi LockBox2和Android代码中,确保密钥符合标准RSA规范。
2. 确认LockBox2的Padding模式
在Delphi端做测试:用私钥加密一段已知明文(如"test123"),然后用OpenSSL验证解密方式:
- 若执行以下命令能解密成功,说明LockBox2用的是PKCS#1 v1.5 Padding:
openssl rsautl -verify -inkey public.pem -pubin -in encrypted.bin -out decrypted.txt -pkcs - 若用
-raw参数能解密,说明LockBox2用的是无Padding的原始RSA:openssl rsautl -verify -inkey public.pem -pubin -in encrypted.bin -out decrypted.txt -raw
3. Android端代码修正
情况A:LockBox2使用PKCS#1 v1.5 Padding
val publicKeyString = "标准PKCS#1公钥的Base64字符串" val registerStringFromService = "服务器返回的加密内容Base64" // 解析PKCS#1格式公钥 val publicKeyBytes = Base64.decode(publicKeyString, Base64.DEFAULT) val seq = ASN1Sequence.fromByteArray(publicKeyBytes) as DLSequence val modulus = seq.elementAt(0) as ASN1Integer val pubExp = seq.elementAt(1) as ASN1Integer val publicKeySpec = RSAPublicKeySpec(modulus.positiveValue, pubExp.positiveValue) val kf = KeyFactory.getInstance("RSA") val publicKey = kf.generatePublic(publicKeySpec) // 初始化Cipher,使用PKCS#1 v1.5 Padding val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") cipher.init(Cipher.DECRYPT_MODE, publicKey) // 解密并转换为字符串 val registerBytesFromService = Base64.decode(registerStringFromService, Base64.DEFAULT) val decryptedBytes = cipher.doFinal(registerBytesFromService) val decryptedString = String(decryptedBytes, Charsets.UTF_8)
情况B:LockBox2使用无Padding的原始RSA
// 密钥解析同上 val publicKey = ... // 初始化无Padding的Cipher val cipher = Cipher.getInstance("RSA/ECB/NoPadding") cipher.init(Cipher.DECRYPT_MODE, publicKey) val registerBytesFromService = Base64.decode(registerStringFromService, Base64.DEFAULT) val decryptedBytes = cipher.doFinal(registerBytesFromService) // 去掉LockBox2可能添加的前置填充字节(通常是0),再转字符串 val trimmedBytes = decryptedBytes.dropWhile { it == 0.toByte() }.toByteArray() val decryptedString = String(trimmedBytes, Charsets.UTF_8)
额外注意事项
- TurboPower LockBox2是老旧库,其RSA实现可能存在字节序差异(比如小端字节序,而Java默认用大端),若解密后内容乱码,可尝试反转
decryptedBytes后再转字符串。 - 标准场景中,私钥加密/公钥解密应该用
Signature类实现签名验证,若后续有机会优化协议,建议调整为「服务器对修改后的内容做签名,客户端验证签名并获取内容」的标准流程。
内容的提问来源于stack exchange,提问作者TomR
相关产品推荐
相关产品推荐

