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;  
        }  

这样,就顺利完成了。

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

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

posted @ 2017-11-19 23:52  糖果的二师兄  阅读(331)  评论(0编辑  收藏  举报