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

    引用:http://blog.csdn.net/dslinmy/article/details/37362661

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

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

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

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

    接下来讲一下实现步骤:

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

    /// <summary>  
           /// 生成公钥、私钥  
           /// </summary>  
           /// <returns>公钥、私钥,公钥键"PUBLIC",私钥键"PRIVATE"</returns>  
           public Dictionary<string, string> createKeyPair()  
           {  
               Dictionary<string, string> keyPair = new Dictionary<string, string>();  
               RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);  
               keyPair.Add("PUBLIC", provider.ToXmlString(false));  
               keyPair.Add("PRIVATE", provider.ToXmlString(true));  
               return keyPair;  
           }  

    如此处生成的公钥为

    <RSAKeyValue>  
        <Modulus>t+56m5jXXonAJAKC7mgkhAZX5gWJTZojbSloLpLBGEWiebFaM+aUUKALfRx83/HaUV79ZiR3zuLJOLBdALx1cmcPk/b9fdNblLmzqi4cfSnfmMLWh05xf+ZS1pKHSKQtui3dfuu+3XH6Ak+S38dpIZUj/hihQQuKysN6GJ9h+c8=  
        </Modulus>  
        <Exponent>AQAB</Exponent>  
    </RSAKeyValue>  

    在客户端(Java)对C#提供的公钥提取Modulus和Exponent

    /** 
         * 返回包含模数modulus和指数exponent的haspMap 
         * @return 
         * @throws MalformedURLException 
         * @throws DocumentException 
         */  
        public static HashMap<String,String> rsaParameters(String xmlPublicKey) throws MalformedURLException, DocumentException{  
            HashMap<String ,String> map = new HashMap<String, String>();   
            Document doc = DocumentHelper.parseText(xmlPublicKey);  
            String mudulus = (String) doc.getRootElement().element("Modulus").getData();  
            String exponent = (String) doc.getRootElement().element("Exponent").getData();  
            map.put("mudulus", mudulus);  
            map.put("exponent", exponent);  
            return map;  
        }  

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

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

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

    public static byte[] decodeBase64(String input) throws Exception{    
            Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");    
            Method mainMethod= clazz.getMethod("decode", String.class);    
            mainMethod.setAccessible(true);    
             Object retObj=mainMethod.invoke(null, input);    
             return (byte[])retObj;    
        }  
          
        /** 
         * 返回RSA公钥 
         * @param modules 
         * @param exponent 
         * @return 
         */  
        public static PublicKey getPublicKey(String modulus, String exponent){  
            try {   
                byte[] m = decodeBase64(modulus);  
                byte[] e = decodeBase64(exponent);  
                BigInteger b1 = new BigInteger(1,m);    
                BigInteger b2 = new BigInteger(1,e);    
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");    
                RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);    
                return (RSAPublicKey) keyFactory.generatePublic(keySpec);    
            } catch (Exception e) {    
                e.printStackTrace();    
                return null;    
            }     
        }  

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

    public static String encrypt(byte[] source, PublicKey publicKey) throws Exception   {  
            String encryptData ="";  
            try {  
                Cipher cipher = Cipher.getInstance("RSA");  
                cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
                int length = source.length;  
                int offset = 0;  
                byte[] cache;  
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();  
                int i = 0;  
                while(length - offset > 0){  
                    if(length - offset > MAXENCRYPTSIZE){  
                        cache = cipher.doFinal(source, offset, MAXENCRYPTSIZE);  
                    }else{  
                        cache = cipher.doFinal(source, offset, length - offset);  
                    }  
                    outStream.write(cache, 0, cache.length);  
                    i++;  
                    offset = i * MAXENCRYPTSIZE;  
                }  
                return encodeBase64(outStream.toByteArray());  
            } catch (NoSuchAlgorithmException e) {  
                e.printStackTrace();  
            } catch (NoSuchPaddingException e) {  
                e.printStackTrace();  
            } catch (InvalidKeyException e) {  
                e.printStackTrace();  
            } catch (IllegalBlockSizeException e) {  
                e.printStackTrace();  
            } catch (BadPaddingException e) {  
                e.printStackTrace();  
            }  
            return encryptData;       
        }  

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

    /// <summary>  
            /// RSA解密  
            /// </summary>  
            /// <param name="encryptData">经过Base64编码的密文</param>  
            /// <param name="privateKey">私钥</param>  
            /// <returns>RSA解密后的数据</returns>  
            public static string decrypt(string encryptData, string privateKey)  
            {  
                string decryptData = "";  
                try  
                {  
                    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();  
                    provider.FromXmlString(privateKey);  
                    byte[] bEncrypt = Convert.FromBase64String(encryptData);                  
                    int length = bEncrypt.Length;  
                    int offset = 0;  
                    string cache ;  
                    int i = 0;  
                    while (length - offset > 0)  
                    {  
                        if (length - offset > MAXDECRYPTSIZE)  
                        {  
                            cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, MAXDECRYPTSIZE), false));  
                        }  
                        else  
                        {  
                            cache = Encoding.UTF8.GetString(provider.Decrypt(getSplit(bEncrypt, offset, length - offset), false));  
                        }  
                        decryptData += cache;  
                        i++;  
                        offset = i*MAXDECRYPTSIZE;  
                    }  
                }  
                catch(Exception e)  
                {  
                    throw e;  
                }  
                return decryptData;  
            }  
      
            /// <summary>  
            /// 截取字节数组部分字节  
            /// </summary>  
            /// <param name="input"></param>  
            /// <param name="offset">起始偏移位</param>  
            /// <param name="length">截取长度</param>  
            /// <returns></returns>  
            private static byte[] getSplit(byte[] input, int offset, int length)  
            {   
                byte[] output = new byte[length];  
                for (int i = offset; i < offset + length; i++)  
                {  
                    output[i - offset] = input[i];  
                }  
                return output;  
            }  

    这样,就顺利完成了。

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

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

  • 相关阅读:
    问题账户需求分析
    2017年秋季个人阅读计划
    读“我们应当怎样做需求分析”有感
    开发体会
    第二阶段个人总结10
    第二阶段个人总结09
    第二阶段个人总结08
    个人进度(13)
    个人进度(12)
    个人进度(11)
  • 原文地址:https://www.cnblogs.com/micenote/p/7863029.html
Copyright © 2011-2022 走看看