将C#RSACryptoServiceProvider翻译成JAVA代码

本文关键字:JAVA 代码 翻译 C#RSACryptoServiceProvider | 更新日期: 2023-09-27 18:26:42

我收到了web服务团队编写的C#代码,该代码公开了我计划使用的一些web服务。我的密码需要用这个代码加密,这样web服务就知道如何在他们端解密它。

using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
    rsa.FromXmlString(publicKey);
    byte[] plainBytes = Encoding.Unicode.GetBytes(clearText);
    byte[] encryptedBytes = rsa.Encrypt(plainBytes, false);
    return Convert.ToBase64String(encryptedBytes);
}

我正在使用Java来使用这个web服务,现在,我在将#C代码翻译成Java代码时遇到了问题,因为该web服务无法正确解密我的密码。

这是我当前失败的尝试:-

// my clear text password
String clearTextPassword = "XXXXX";
// these values are provided by the web service team
String modulusString = "...";
String publicExponentString = "...";
BigInteger modulus = new BigInteger(1, Base64.decodeBase64(modulusString.getBytes("UTF-8")));
BigInteger publicExponent = new BigInteger(1, Base64.decodeBase64(publicExponentString.getBytes("UTF-8")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(Base64.encodeBase64(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));

我做错了什么?非常感谢。

2013-08-07-更新

我读过这个网站,我意识到我的模值和公共指数值不是十六进制的。因此,我稍微修改了一下代码,并尝试使用@Dev提到的RSA/ECB/PKCS1PADDING

// my clear text password
String clearTextPassword = "XXXXX";
// these are the actual values I get from the web service team
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
Base64 base64Encoder = new Base64();
String modulusHex = new String(Hex.encodeHex(modulusString.getBytes("UTF-8")));
String publicExponentHex = new String(Hex.encodeHex(publicExponentString.getBytes("UTF-8")));
BigInteger modulus = new BigInteger(modulusHex, 16);
BigInteger publicExponent = new BigInteger(publicExponentHex);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
String encodedEncryptedPassword = new String(base64Encoder.encode(cipher.doFinal(clearTextPassword.getBytes("UTF-8"))));

当我访问Web服务时,我会收到以下错误:"要解密的数据超过了128字节的最大模数。"明文密码似乎仍未正确加密。

非常感谢您的任何帮助或建议。谢谢

2013-08-09-解决方案

我在下面发布了我的最终工作解决方案。

将C#RSACryptoServiceProvider翻译成JAVA代码

找到了解决方案。

String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decodeBase64(modulusString);
byte[] exponentBytes = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = clearTextPassword.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.encodeBase64String(cipherData);

根据MSDN关于RSACryptoServiceProvider.Encrypt的文档,当第二个参数是false时,密码使用PKCS#1 v1.5填充。所以马上你的密码规范就不正确了。

请改用RSA/ECB/PKCS1PADDING

在第二个代码示例中,您将密钥材料转换为大量内容,但您破坏了它,这最终会使您的密码认为您拥有的密钥材料比实际拥有的更多,并使您的消息过长(这会引发您的错误),以及另一端的解密密码无法理解。直接转换为字节数组并将其传递给BigInteger

String modulusString = "...";
String publicExponentString = "...";
byte[] mod = Base64.decodeBase64(modulusString);
byte[] e = Base64.decodeBase64(publicExponentString);
BigInteger modulus = new BigInteger(1, mod);
BigInteger publicExponent = new BigInteger(1, e);

使用RSA/ECB/OAEPWithSHA-1AndMGF1Padding似乎是解决此问题的方法。

别忘了这些替代品。

编码到Base64使用System.Convert将输入转换为Base64。

将+替换为-和/替换为_。示例:Foo+bar/=== becomes Foo-bar_===.

将字符串末尾的任意数字=替换为一个整数,表示它们的数量。示例:Foo-bar_===变为Foo-bar_3。

从Base64解码用相同数量的=符号替换字符串末尾的数字。示例:Foo-bar_3 becomes Foo-bar_===.

将-替换为+,将_替换为/。示例:Foo-bar_=== becomes Foo+bar/===.

使用System.Convert对Base64中经过预处理的输入进行解码。