2013-08-06 60 views
10

我被给出了由Web服务团队编写的这个C#代码,该代码公开了一些我打算使用的Web服务。我的密码需要使用此代码进行加密,以便Web服务知道如何对其进行解密。将C#RSACryptoServiceProvider转换为JAVA代码

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年8月7日 - UPDATE

我读this website,我意识到,我的模值和公共指数值不是十六进制。所以,我修改了一下我的代码,并用@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年8月9日 - 解决方案

我下面贴我的最终工作方案。

回答

8

找到了解决方案。

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); 
+0

我有一种感觉,最终会成为登录协议问题而不是密码。我们几乎涵盖了其他一切。很高兴你想出来了。 – Dev

+0

当我看到'Encoding.Unicode.GetBytes(String)'我正在考虑'UTF-8',而不是'UTF-16LE',显然是不正确的假设,所以很好的理解你。 – Dev

+0

谢谢你 - 解决了我的问题。看起来像.NET中的人经常使用Encoding.Unicode.GetBytes/Encoding.Unicode.GetString没有意识到它不是UTF-8。 – mvmn

1

根据有关RSACryptoServiceProvider.Encrypt的MSDN文档,当第二个参数是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); 
+0

我已经尝试过“RSA/ECB/PKCS1PADDING”,还有“RSA”的密码。两个失败。 – limc

+0

@limc谁在使用Base64类? – Dev

+0

我目前使用的是Commons Codec。我最初使用Spring的编解码器,然后我认为这会是问题,所以我切换到了Commons Codec ...但它们产生了相同的结果。 – limc