客户端无法解密在服务器上使用RSA加密的内容

3
我们有一个客户端服务器系统,其中客户端(Android手机)和服务器(Spring)都使用java.security.KeyFactory来获取java.security.KeyFactory的实例,如下所示:
KeyFactory factory = KeyFactory.getInstance("RSA");

但是如果我们这样做,当我们使用该工厂对数据进行加密时,服务器会给出不同的输出,客户端也会得到不同的输出。当我们检查提供者时,发现服务器使用的是SunRsaSign,而客户端使用的是OpenSSLRSA。因此,我们尝试在客户端上使用以下设置来使其与服务器一致:

KeyFactory factory = KeyFactory.getInstance("RSA", "SunRsaSign");

但是我们遇到了 java.security.NoSuchProviderException 错误。同样,当我们尝试在服务器上设置OpenSSLRSA时,他们也会遇到相同的错误。

加密的完整代码在服务器和客户端上都是相同的,如下所示:

String pubKey = "<key here>"
byte[] keyData = DatatypeConverter.parseHexBinary(pubKey);
System.out.println("key data" + Arrays.toString(keyData));
KeyFactory factory = KeyFactory.getInstance("RSA");
//System.out.println("provide = " + factory.getProvider());
PublicKey pub = factory.generatePublic(new X509EncodedKeySpec(keyData));
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pub);
byte[] secretMessageBytes = msg.getBytes(StandardCharsets.UTF_8);
System.out.println("secret msg" +Arrays.toString(secretMessageBytes));
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
System.out.println("enc data" +Arrays.toString(encryptedMessageBytes));

生成的encryptedMessageBytes不同,这会成为问题吗?我想这是因为不同平台使用了不同的提供程序。

请问有人能帮我设置KeyFactory的提供程序或者如何解决解密错误(javax.crypto.BadPaddingException)吗?


还要注意,RSA 加密 为相同的输入数据生成不同的密文。这本身并不意味着存在问题。 - Topaco
@user9014097 是的,解密失败了。如果我们硬编码使用相同的encryptedMessageBytes,则可以成功。 - Tarun Deep Attri
1
尝试使用Cipher.getInstance("RSA/ECB/PKCS1Padding")。可能每一侧的填充默认值不同。 - Topaco
@user9014097 将提到的代码添加到客户端侧即可使其正常工作。您能否将其添加为答案,以便我可以接受它?您能否在答案中解释一下为什么之前它没有工作,现在为什么可以工作? - Tarun Deep Attri
1
我已经详细阐述了我的评论并将其发布为答案。 - Topaco
显示剩余4条评论
1个回答

1

发布的代码在Android和Java/Spring两侧都使用,只指定了算法而没有指定填充方式来实例化密码:

Cipher.getInstance("RSA")

没有显式指定填充时,使用两侧提供者的默认填充。然而,不同的提供者通常定义不同的默认填充(例如NoPaddingPKCS1PaddingOAEPPadding)。不同的填充会导致解密失败,因为成功解密的前提是与加密使用相同的填充。

为避免此类情况,在实例化密码器时应始终指定填充,特别是在跨平台环境中,例如:

Cipher.getInstance("RSA/ECB/PKCS1Padding")

在左边是算法,在右边是填充。请注意,中间部分(ECB)对于RSA没有意义(这是使用对称加密方案指定中间操作模式的副作用,该模式未定义为非对称加密)。修复显式指定填充的问题证明了填充确实是问题所在。

我只能猜测您的环境中使用的默认填充。

由于我的环境中没有OpenSSLRSA提供程序,因此无法进行测试。在我的机器上,Android(API级别28,P)应用AndroidOpenSSL提供程序(又名Conscrypt)。这默认为NoPadding,而Java的SunJCE提供程序默认为PKCS1Padding。在Android上加密并在Java上解密会导致异常(或其他方向上未删除的填充)。

您可以按以下方式确定环境的默认填充:使用默认填充进行加密(仅指定算法),并在解密时变化填充方式,直到成功解密并还原明文。


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