c#和JAVA之间使用RSA加解密交互1

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 09:33   11   0

这里,讲一下RSA算法加解密在C#Java之间交互的问题,这两天纠结了很久,也看了很多其他人写的文章,颇受裨益,但没能解决我的实际问题,终于,还是被我捣鼓出来了。

首先,介绍一下写这代码的目的:完成webService验证问题,服务器端采用C#开发,客户端采用Java开发。服务器端给客户端提供公钥,已进行数据加密,客户端加密后提数据提交给服务器,服务器用私钥对数据解密,进行验证。

这里遇到的主要问题是C#RSACryptoServiceProvider类产生的公钥、私钥都是xml字符串数据,而javaRSA算法要求的Modulus、Exponent都是BigInteger类型,两者间的转换才是问题所在。

关于JavaC#各自独立的进行RSA加密解密,大家可以看整两篇文章,javaRSA加密解密实现()C#RSA加密解密和签名与验证的实现

接下来讲一下实现步骤:

首先由C#RSACryptoServiceProvider类生成公钥、私钥

[csharp]view plaincopy在CODE上查看代码片派生到我的代码片
  1. ///<summary>
  2. ///生成公钥、私钥
  3. ///</summary>
  4. ///<returns>公钥、私钥,公钥键"PUBLIC",私钥键"PRIVATE"</returns>
  5. publicDictionary<string,string>createKeyPair()
  6. {
  7. Dictionary<string,string>keyPair=newDictionary<string,string>();
  8. RSACryptoServiceProviderprovider=newRSACryptoServiceProvider(1024);
  9. keyPair.Add("PUBLIC",provider.ToXmlString(false));
  10. keyPair.Add("PRIVATE",provider.ToXmlString(true));
  11. returnkeyPair;
  12. }

如此处生成的公钥为

  1. <RSAKeyValue>
  2. <Modulus>t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8=
  3. </Modulus>
  4. <Exponent>AQAB</Exponent>
  5. </RSAKeyValue>
在客户端(Java)对C#提供的公钥提取Modulus和Exponent
  1. /**
  2. *返回包含模数modulus和指数exponent的haspMap
  3. *@return
  4. *@throwsMalformedURLException
  5. *@throwsDocumentException
  6. */
  7. publicstaticHashMap<String,String>rsaParameters(StringxmlPublicKey)throwsMalformedURLException,DocumentException{
  8. HashMap<String,String>map=newHashMap<String,String>();
  9. Documentdoc=DocumentHelper.parseText(xmlPublicKey);
  10. Stringmudulus=(String)doc.getRootElement().element("Modulus").getData();
  11. Stringexponent=(String)doc.getRootElement().element("Exponent").getData();
  12. map.put("mudulus",mudulus);
  13. map.put("exponent",exponent);
  14. returnmap;
  15. }

用ModulusExponent产生公钥RSAPublicKeyjava

这里有个关键步骤先对MudolusExponent进行Base64解码,这个是由于C#生成的密钥对,其参数已经过Base64编码成String类型,而java RSA参数是未经base64编码的byte[]类型

至于Base64编码、解码方法,参考这篇文章,java 编码和解码,想详细。

  1. publicstaticbyte[]decodeBase64(Stringinput)throwsException{
  2. Classclazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
  3. MethodmainMethod=clazz.getMethod("decode",String.class);
  4. mainMethod.setAccessible(true);
  5. ObjectretObj=mainMethod.invoke(null,input);
  6. return(byte[])retObj;
  7. }
  8. /**
  9. *返回RSA公钥
  10. *@parammodules
  11. *@paramexponent
  12. *@return
  13. */
  14. publicstaticPublicKeygetPublicKey(Stringmodulus,Stringexponent){
  15. try{
  16. byte[]m=decodeBase64(modulus);
  17. byte[]e=decodeBase64(exponent);
  18. BigIntegerb1=newBigInteger(1,m);
  19. BigIntegerb2=newBigInteger(1,e);
  20. KeyFactorykeyFactory=KeyFactory.getInstance("RSA");
  21. RSAPublicKeySpeckeySpec=newRSAPublicKeySpec(b1,b2);
  22. return(RSAPublicKey)keyFactory.generatePublic(keySpec);
  23. }catch(Exceptione){
  24. e.printStackTrace();
  25. returnnull;
  26. }
  27. }

获得公钥后就可以进行RSA加密处理了,这里还有一点需要提的是,RSA加密解密都有最大长度限制,加密最大长度为117字节,解密最大长度是128字节,此外,此处加密得到的数据是经过Base64编码处理的

  1. publicstaticStringencrypt(byte[]source,PublicKeypublicKey)throwsException{
  2. StringencryptData="";
  3. try{
  4. Ciphercipher=Cipher.getInstance("RSA");
  5. cipher.init(Cipher.ENCRYPT_MODE,publicKey);
  6. intlength=source.length;
  7. intoffset=0;
  8. byte[]cache;
  9. ByteArrayOutputStreamoutStream=newByteArrayOutputStream();
  10. inti=0;
  11. while(length-offset>0){
  12. if(length-offset>MAXENCRYPTSIZE){
  13. cache=cipher.doFinal(source,offset,MAXENCRYPTSIZE);
  14. }else{
  15. cache=cipher.doFinal(source,offset,length-offset);
  16. }
  17. outStream.write(cache,0,cache.length);
  18. i++;
  19. offset=i*MAXENCRYPTSIZE;
  20. }
  21. returnencodeBase64(outStream.toByteArray());
  22. }catch(NoSuchAlgorithmExceptione){
  23. e.printStackTrace();
  24. }catch(NoSuchPaddingExceptione){
  25. e.printStackTrace();
  26. }catch(InvalidKeyExceptione){
  27. e.printStackTrace();
  28. }catch(IllegalBlockSizeExceptione){
  29. e.printStackTrace();
  30. }catch(BadPaddingExceptione){
  31. e.printStackTrace();
  32. }
  33. returnencryptData;
  34. }

加密后的数据提交给C#服务器端进行解密,当然,这里也要注意最大长度限制问题

[csharp]view plaincopy在CODE上查看代码片派生到我的代码片
  1. ///<summary>
  2. ///RSA解密
  3. ///</summary>
  4. ///<paramname="encryptData">经过Base64编码的密文</param>
  5. ///<paramname="privateKey">私钥</param>
  6. ///<returns>RSA解密后的数据</returns>
  7. publicstaticstringdecrypt(stringencryptData,stringprivateKey)
  8. {
  9. stringdecryptData="";
  10. try
  11. {
  12. RSACryptoServiceProviderprovider=newRSACryptoServiceProvider();
  13. provider.FromXmlString(privateKey);
  14. byte[]bEncrypt=Convert.FromBase64String(encryptData);
  15. intlength=bEncrypt.Length;
  16. intoffset=0;
  17. stringcache;
  18. inti=0;
  19. while(length-offset>0)
  20. {
  21. if(length-offset>MAXDECRYPTSIZE)
  22. {
  23. cache=Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt,offset,MAXDECRYPTSIZE),false));
  24. }
  25. else
  26. {
  27. cache=Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt,offset,length-offset),false));
  28. }
  29. decryptData+=cache;
  30. i++;
  31. offset=i*MAXDECRYPTSIZE;
  32. }
  33. }
  34. catch(Exceptione)
  35. {
  36. throwe;
  37. }
  38. returndecryptData;
  39. }
  40. ///<summary>
  41. ///截取字节数组部分字节
  42. ///</summary>
  43. ///<paramname="input"></param>
  44. ///<paramname="offset">起始偏移位</param>
  45. ///<paramname="length">截取长度</param>
  46. ///<returns></returns>
  47. privatestaticbyte[]getSplit(byte[]input,intoffset,intlength)
  48. {
  49. byte[]output=newbyte[length];
  50. for(inti=offset;i<offset+length;i++)
  51. {
  52. output[i-offset]=input[i];
  53. }
  54. returnoutput;
  55. }
这样,就顺利完成了。

经过测试,这样做的确得到了正确的结果。

若是有什么地方有问题,还望大家指正!

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP