Android RSA解密失败/服务器端加密(openssl_public_encrypt)

3
我正在尝试在Android应用程序中使用在我的设备上生成的RSA密钥解密字符串。加密是通过一个php服务完成的,该服务使用应用程序提供的公共RSA密钥。我的问题出在解密过程中,它一直失败。
我正在进行以下操作:
- 在Android上生成KeyPair(使用KeyPairGenerator.getInstance(“RSA”))-> OK - 两个密钥(公钥和私钥)在编码为Base64.encode(pubKey.getEncoded())并将其相同的私钥存储到文件中后保存。-> OK - 当我调用web服务时,在POST变量中传递我的公钥(以base 64格式)-> OK - web服务(php服务)使用公钥用openssl_public_encrypt函数加密短字符串,加密后的字符串转换为base64。 -> 看起来不错,该函数没有返回FALSE。 - 应用程序检索服务的结果,并对其进行解码(Base64.decode())-> OK(我已经检查,接收到的字节与由openssl_public_encrypt()函数生成的字节匹配) - 最后一件事是解密这个字符串,我正在执行以下操作:->NOT OK
Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedBytes = cipher.doFinal(cryptedBytes); String decryptedString = new String(decryptedBytes); System.out.println(decryptedString);
解密的结果与我的原始字符串不匹配。
我有什么遗漏的吗?

Cipher.getInstance("RSA") 默认使用“纯文本RSA”——没有填充——这是一点都不安全的。我不知道PHP代码使用什么样的填充,因为你没有展示它,但你应该在两端都改为OAEP填充。 - ntoskrnl
1
@ntoskrnl 你可以把这个改成一个答案。请注意,默认的“RSA/NONE/NoPadding”仅适用于Bouncy Castle,Java SE - 更准确地说是Sun JCE - 默认为“RSA/ECB/PKCS1Padding”。 - Maarten Bodewes
@owlstead 很好的观点 - 还说明了明确声明模式的重要性,而不是依赖于在实现之间不同的默认值。 - ntoskrnl
1个回答

11

OpenSSL默认使用padding = OPENSSL_PKCS1_PADDING。 因此,为了在两侧使用相同的填充机制,您应该使用Cipher.getInstance("RSA/ECB/PKCS1Padding")。这也是您可以在Java SE中使用的内容。

请注意,在密码学中依赖默认操作模式非常危险。许多实现具有不同的默认值,并且很难查找。因此,始终尝试完全指定要使用的算法/模式。

您可以尝试其他RSA填充模式,但请注意-不幸的是-Android已从他们改编的Bouncy Castle源代码中禁用了许多算法和别名。


[编辑] 现在强烈建议使用OAEP填充,或使用RSA-KEM的混合加密。


感谢@owlstead,使用getInstance(“RSA / ECB / PKCS1Padding”),我的服务器端脚本可以正确解密由我的Android应用程序加密的内容。但是,如果我使用getInstance(“RSA / None / PKCS1Padding”),则会出现“RSA块的数据过多”异常(尽管在服务器端解密不起作用)。有什么想法吗? - ericn
1
这似乎是混合加密的一个明显例子。RSA加密不应该单独使用;通常你应该加密一个新生成的随机AES密钥,然后用该AES密钥加密数据(例如使用CBC操作模式),这样你就可以加密几乎无限量的明文。 - Maarten Bodewes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接