zoukankan      html  css  js  c++  java
  • C# Java间进行RSA加密解密交互

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

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

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

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

    接下来讲一下实现步骤:

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

    [csharp] view plain copy
     
    1. /// <summary>  
    2.        /// 生成公钥、私钥  
    3.        /// </summary>  
    4.        /// <returns>公钥、私钥,公钥键"PUBLIC",私钥键"PRIVATE"</returns>  
    5.        public Dictionary<string, string> createKeyPair()  
    6.        {  
    7.            Dictionary<string, string> keyPair = new Dictionary<string, string>();  
    8.            RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);  
    9.            keyPair.Add("PUBLIC", provider.ToXmlString(false));  
    10.            keyPair.Add("PRIVATE", provider.ToXmlString(true));  
    11.            return keyPair;  
    12.        }  

    如此处生成的公钥为

    [html] view plain copy
     
    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

    [java] view plain copy
     
    1. /** 
    2.      * 返回包含模数modulus和指数exponent的haspMap 
    3.      * @return 
    4.      * @throws MalformedURLException 
    5.      * @throws DocumentException 
    6.      */  
    7.     public static HashMap<String,String> rsaParameters(String xmlPublicKey) throws MalformedURLException, DocumentException{  
    8.         HashMap<String ,String> map = new HashMap<String, String>();   
    9.         Document doc = DocumentHelper.parseText(xmlPublicKey);  
    10.         String mudulus = (String) doc.getRootElement().element("Modulus").getData();  
    11.         String exponent = (String) doc.getRootElement().element("Exponent").getData();  
    12.         map.put("mudulus", mudulus);  
    13.         map.put("exponent", exponent);  
    14.         return map;  
    15.     }  

    用Modulus和Exponent产生公钥RSAPublicKey(java)

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

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

    [java] view plain copy
     
    1. public static byte[] decodeBase64(String input) throws Exception{    
    2.         Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");    
    3.         Method mainMethod= clazz.getMethod("decode", String.class);    
    4.         mainMethod.setAccessible(true);    
    5.          Object retObj=mainMethod.invoke(null, input);    
    6.          return (byte[])retObj;    
    7.     }  
    8.       
    9.     /** 
    10.      * 返回RSA公钥 
    11.      * @param modules 
    12.      * @param exponent 
    13.      * @return 
    14.      */  
    15.     public static PublicKey getPublicKey(String modulus, String exponent){  
    16.         try {   
    17.             byte[] m = decodeBase64(modulus);  
    18.             byte[] e = decodeBase64(exponent);  
    19.             BigInteger b1 = new BigInteger(1,m);    
    20.             BigInteger b2 = new BigInteger(1,e);    
    21.             KeyFactory keyFactory = KeyFactory.getInstance("RSA");    
    22.             RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);    
    23.             return (RSAPublicKey) keyFactory.generatePublic(keySpec);    
    24.         } catch (Exception e) {    
    25.             e.printStackTrace();    
    26.             return null;    
    27.         }     
    28.     }  

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

    [java] view plain copy
     
    1. public static String encrypt(byte[] source, PublicKey publicKey) throws Exception   {  
    2.         String encryptData ="";  
    3.         try {  
    4.             Cipher cipher = Cipher.getInstance("RSA");  
    5.             cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
    6.             int length = source.length;  
    7.             int offset = 0;  
    8.             byte[] cache;  
    9.             ByteArrayOutputStream outStream = new ByteArrayOutputStream();  
    10.             int i = 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.             return encodeBase64(outStream.toByteArray());  
    22.         } catch (NoSuchAlgorithmException e) {  
    23.             e.printStackTrace();  
    24.         } catch (NoSuchPaddingException e) {  
    25.             e.printStackTrace();  
    26.         } catch (InvalidKeyException e) {  
    27.             e.printStackTrace();  
    28.         } catch (IllegalBlockSizeException e) {  
    29.             e.printStackTrace();  
    30.         } catch (BadPaddingException e) {  
    31.             e.printStackTrace();  
    32.         }  
    33.         return encryptData;       
    34.     }  

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

    [csharp] view plain copy
     
    1. /// <summary>  
    2.         /// RSA解密  
    3.         /// </summary>  
    4.         /// <param name="encryptData">经过Base64编码的密文</param>  
    5.         /// <param name="privateKey">私钥</param>  
    6.         /// <returns>RSA解密后的数据</returns>  
    7.         public static string decrypt(string encryptData, string privateKey)  
    8.         {  
    9.             string decryptData = "";  
    10.             try  
    11.             {  
    12.                 RSACryptoServiceProvider provider = new RSACryptoServiceProvider();  
    13.                 provider.FromXmlString(privateKey);  
    14.                 byte[] bEncrypt = Convert.FromBase64String(encryptData);                  
    15.                 int length = bEncrypt.Length;  
    16.                 int offset = 0;  
    17.                 string cache ;  
    18.                 int i = 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(Exception e)  
    35.             {  
    36.                 throw e;  
    37.             }  
    38.             return decryptData;  
    39.         }  
    40.   
    41.         /// <summary>  
    42.         /// 截取字节数组部分字节  
    43.         /// </summary>  
    44.         /// <param name="input"></param>  
    45.         /// <param name="offset">起始偏移位</param>  
    46.         /// <param name="length">截取长度</param>  
    47.         /// <returns></returns>  
    48.         private static byte[] getSplit(byte[] input, int offset, int length)  
    49.         {   
    50.             byte[] output = new byte[length];  
    51.             for (int i = offset; i < offset + length; i++)  
    52.             {  
    53.                 output[i - offset] = input[i];  
    54.             }  
    55.             return output;  
    56.         }  

    这样,就顺利完成了。

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

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

    ----------------------------------------------------------------------------------------

    C# Java间进行RSA加密解密交互(二)

    C# Java间进行RSA加密解密交互(三)

  • 相关阅读:
    刷题系列
    元类编程
    Python内置方法与面向对象知识点进阶系列
    json反序列化的时候字符串为单引号的一个坑
    刨根问底,完美解决Django2版本连接MySQL报错的问题
    使用mkdocs撰写技术文档并免费部署上线
    关于Python的源文件编译看这一篇就够了
    SQL查询where语句后面字符串大小写问题
    configparser模块获取settings.ini文件中的配置数据
    Sharepoint 2013列表视图和字段权限扩展插件(免费下载)!
  • 原文地址:https://www.cnblogs.com/amylis_chen/p/8604963.html
Copyright © 2011-2022 走看看