Java和C# RSA加密密钥互通以及密文字节信息大于117位或128位的终极解决办法

转自: http://blog.csdn.net/wenfengzhuo/article/details/7925433

 

Java和C# RSA加密密钥互通以及密文字节信息大于117位或128位的终极解决办法

分类: 加密解密   939人阅读  评论(0)  收藏  举报

目前在做RSA解密的过程中,发现C#和java的加密解密过程要想十分顺畅的实现确实有点困难,而且在网上看到大把大把的帖子致力于解决这个神一般的问题,但是别人能解决,到你这可能就不适用了。究其原因,是因为在实现这个问题的过程中,会有各种各样的小错误,就这么一个错误,葬送了你大把的时间和精力。

1、错误列举如下,不能详尽:

A) 密钥不同。这个是最难最繁琐的问题。java中密钥通常是封装在Key里面的,比如PublicKey,PrivateKey,将其转化为string后,会发现这些key是有不同的大整数(BigInteger)组成的;C#中密钥的导入其实普遍就一种情况,FromXmlString(),也就是说在C#中的,你的值是以xml的形式存在的。那么这个时候怎样保证一堆大整数和一个xml组成的key值相同,是很关键的,一旦不能保证,也许就会在c#中出现--“不正确的数据”这种恶心的错误。

B) 编码有误。这个问题很好解决,但是也容易忽略。因为在加密解密过程中你要乐此不疲的编码解码(encode,decode)。要编成什么样的码呢?Base64.用公钥加密后,在返回一个加密的String之前,你得首先将其encode成Base64编码,然后再用私钥解密时,你同样得将这个Base64编码的密文转化回来。估计这个问题很少有人会为此烦恼。

C) 加密密文不能保证正确。 也就是说,你在java端将信息加密后,也许你的密文在C#端不能被正确解密。这个时候就可能是加密密文不正确导致的错误。这个时候你得看看你在加密时,有没有用一种C#里面没用到的东西,我一开始的时候,使用了BouncyCastleProvider()来初始化cipher,结果密文始终不能成功解密。另外值得一提的是,你为了让密文在加密端和解密端是一致的,你反复查看了密文的字节信息,发现他们始终不一样。这个时候不要慌,这里有个很重要的只是点,在java中Base64的String的字节数组是支持有符号的字节的,然而在C#中的就是无符号的了,因此你如果对比出来的结果是下面这个样子的,就不必惊慌了,你传值很正确。

[plain]  view plain copy print ?
  1. java:[12,23,-1,-2....]  
  2. C#:[12,23,255,254.....]  
发现没,其实java中负数的字节加上C#中的正数正好是256,所以不要慌了,在内存中他们都是一样一样的。

D) 密文超过117位。似乎当你加密超过117位以后,不管是C#还是java都会出现错误,说你不该加密这么多信息。这个问题不难解决。

E)解密密文超过128位。当你解密超过128位以后,你同样会得到相同的待遇,你不能一次解决太多信息。这个问题应该也很好解决。

2、那么下面我们就来一步一步的解决以上这些恶心的问题。

A) 首先我们生成密钥,一定得保证密钥能过互通。

a)在java端生成KeyPair:

[java]  view plain copy print ?
  1. /** 
  2.      * 生成RSA密钥对 
  3.      *  
  4.      * @param keyLength 
  5.      * 密钥长度,范围:512~2048 
  6.      * @author wenfengzhuo 
  7.      */  
  8.     public static KeyPair generateRSAKeyPair(int keyLength) {  
  9.         try {  
  10.             KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");  
  11.             kpg.initialize(keyLength);  
  12.             return kpg.genKeyPair();  
  13.         } catch (NoSuchAlgorithmException e) {  
  14.             return null;  
  15.         }  
  16.     }  


b)得到xml形式的密钥,扔给你两个函数,这两个函数是所有网上提供的最精华的,如果出错而且你解决不了,只能说你人品有问题。

[html]  view plain copy print ?
  1. /**  
  2.   * 将私钥转化为xml格式  
  3.   *   
  4.   * @param encodedPrivkey=privateKey.getEncoded()  
  5.   *   
  6.   * @author wenfengzhuo  
  7.   */  
  8.  private static String  privatekeyinfoToXMLRSAPriKey(byte[] encodedPrivkey) {  
  9.     try{  
  10.     StringBuffer buff = new StringBuffer(1024);  
  11.   
  12.     PKCS8EncodedKeySpec pvkKeySpec = new PKCS8EncodedKeySpec(encodedPrivkey);  
  13.     KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
  14.     RSAPrivateCrtKey pvkKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(pvkKeySpec);  
  15.   
  16.     buff.append("<RSAKeyValue>") ;  
  17.     buff.append("<Modulus>" + b64encode(removeMSZero(pvkKey.getModulus().toByteArray())) + "</Modulus>");  
  18.   
  19.     buff.append("<Exponent>" + b64encode(removeMSZero(pvkKey.getPublicExponent().toByteArray())) + "</Exponent>");  
  20.   
  21.     buff.append("<P>" + b64encode(removeMSZero(pvkKey.getPrimeP().toByteArray())) + "</P>");  
  22.   
  23.     buff.append("<Q>" + b64encode(removeMSZero(pvkKey.getPrimeQ().toByteArray())) + "</Q>");  
  24.   
  25.     buff.append("<DP>" +b64encode(removeMSZero(pvkKey.getPrimeExponentP().toByteArray())) + "</DP>");  
  26.   
  27.     buff.append("<DQ>" + b64encode(removeMSZero(pvkKey.getPrimeExponentQ().toByteArray())) + "</DQ>");  
  28.   
  29.     buff.append("<InverseQ>" + b64encode(removeMSZero(pvkKey.getCrtCoefficient().toByteArray())) + "</InverseQ>");  
  30.   
  31.     buff.append("<D>" + b64encode(removeMSZero(pvkKey.getPrivateExponent().toByteArray())) + "</D>");  
  32.     buff.append("</RSAKeyValue>") ;  
  33.   
  34.     return buff.toString();  
  35.     }  
  36.     catch(Exception e)   
  37.     {System.err.println(e);   
  38.      return null ;  
  39.     }     
  40.  }  
其实公钥也是从私钥中获取的,你熟悉C#中RSA的公私钥的xml格式你就会明白这个函数的意图。
[java]  view plain copy print ?
  1. /** 
  2.   * 将公钥转化为xml格式 
  3.   *  
  4.   * @param encodedPrivkey=privateKey.getEncoded() 
  5.   *  
  6.   * @author wenfengzhuo 
  7.   */  
  8.  private static String  privatekeyinfoToXMLRSAPubKey(byte[] encodedPrivkey) {  
  9.     try{  
  10.     StringBuffer buff = new StringBuffer(1024);  
  11.   
  12.     PKCS8EncodedKeySpec pvkKeySpec = new PKCS8EncodedKeySpec(encodedPrivkey);  
  13.     KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
  14.     RSAPrivateCrtKey pvkKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(pvkKeySpec);  
  15.     buff.append("<RSAKeyValue>") ;  
  16.     buff.append("<Modulus>" + b64encode(removeMSZero(pvkKey.getModulus().toByteArray())) + "</Modulus>");  
  17.     buff.append("<Exponent>" + b64encode(removeMSZero(pvkKey.getPublicExponent().toByteArray())) + "</Exponent>");  
  18.     buff.append("</RSAKeyValue>") ;  
  19.     return buff.toString();  
  20.     }  
  21.     catch(Exception e)   
  22.     {System.err.println(e);   
  23.      return null ;  
  24.     }     
  25.  }  
有一个函数十分重要,真舍不得拿出来。
[java]  view plain copy print ?
  1. /** 
  2.   * 实现java和C# key的byte[]值互通 
  3.   *  
  4.   * @author wenfengzhuo 
  5.   */  
  6.  private static byte[] removeMSZero(byte[] data) {  
  7.     byte[] data1 ;  
  8.     int len = data.length;  
  9.     if (data[0] == 0)   
  10.     {  
  11.         data1 = new byte[data.length-1] ;  
  12.         System.arraycopy(data, 1, data1, 0, len-1);  
  13.     }  
  14.     else  
  15.         data1 = data;  
  16.        
  17.     return data1;  
  18.  }  

c) 从xml中得到密钥的modulus值和delement值,同时要知道exponent为AQAB。例如:

[java]  view plain copy print ?
  1. <RSAKeyValue><Modulus>gxfQnjxSq4vc1QPxzu/zHx/CDNDKvOHY4WWT26CyOpt/vd1RI7KSV0S5xI4IaDjW4AKBa96dKSmC6SXAMWFcydjH8OBHd2uevW/2JT8aNBxbXa7ZT5BSAGAP2Llu4p9K6G6MBS0BpLwyPY/I96+QjfBOpr2km1iHfejLDMzqZh8=</Modulus><Exponent>AQAB</Exponent><P>xhFjNVPZjlq6qacH+Jndv0pjVRhl2zLRvbU43CeaKn7rZlIjmLke/Kaa7n85RDa8WszV/qzLRGyLFLXStDe4FQ==</P><Q>qW+Wr430WHUGjy93jKZwcqhOHQMwvrpALIzRr2wqrts/EjUuu1TT7q0jORaJwszdyhzV5rhP27Musg9e9czeYw==</Q><DP>b9jBvQtsfwadAdBgn/HDVpIbJaJxYiaAajA7u5ZhaD3jkBQyJbvj06gBNUyCehKuqlehP/8ziaABqeBC66HLQQ==</DP><DQ>UpgV4XAWe4RYdTYTVdnQmOPYaCYk/eR24gCnOpdE20X/IdYQ+LqqMP/vWqXU88PAHgUedEDGlPdsvNnnVXTPtw==</DQ><InverseQ>l6f5qQqS4pX+Ox/d4Xokh8oz0JStFdlU+O8rJk1mKc69J+7c3LvqQlBm9ilkrX64NdaC2Q0WaGoVb62ZYaaLJg==</InverseQ><D>dOYisUgnjDPvW8Dlu1v7mBCQ0S7K6z4WUYBsJliZNmv9sIiP115g4xM0+NKTKqUs75ssX82mpdMPmuqABUjGHSzMDGhH0q/Mh9p8DSO67Cxo5NZRdIO7o21HdgksMNOXBz5AGGAgh7sy/bgkmVuIPcptJMb7cVJbLeoNAMmQ1uk=</D></RSAKeyValue>  
其中modulus为<Modulus></Modulus>中的数据,delement为<D></D>中的数据,将这些值保存下来。

d) 通过modulus和delement值得到java中的publicKey和privateKey。

[java]  view plain copy print ?
  1. /** 
  2.      * 获取publicKey 
  3.      *  
  4.      * @author wenfengzhuo 
  5.      */  
  6.     public static PublicKey getPublicKey() throws InvalidKeySpecException, NoSuchAlgorithmException  
  7.     {  
  8.         byte[] modulusBytes = Base64Helper.decode(module);     
  9.         byte[] exponentBytes = Base64Helper.decode(exponentString);     
  10.         BigInteger modulus = new BigInteger(1, modulusBytes);     
  11.         BigInteger exponent = new BigInteger(1, exponentBytes);     
  12.         RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);     
  13.         KeyFactory fact = KeyFactory.getInstance("RSA");     
  14.         PublicKey pubKey = fact.generatePublic(rsaPubKey);  
  15.         return pubKey;  
  16.     }  

[java]  view plain copy print ?
  1. /** 
  2.      * 获取privateKey 
  3.      *  
  4.      * @author wenfengzhuo 
  5.      */  
  6.     public static PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException  
  7.     {  
  8.      byte[] expBytes = Base64Helper.decode(delement);     
  9.          byte[] modBytes = Base64Helper.decode(module);     
  10.          BigInteger modules = new BigInteger(1, modBytes);     
  11.          BigInteger exponent = new BigInteger(1, expBytes);     
  12.          KeyFactory factory = KeyFactory.getInstance("RSA");     
  13.          RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modules, exponent);     
  14.          PrivateKey privKey = factory.generatePrivate(privSpec);    
  15.          return privKey;  
  16.     }  
关于密钥就是这么些内容了,中间过程得自己思考一下,不能全部照抄。提供Base64Helper类吧,虽然挺简单的。

[java]  view plain copy print ?
  1. /** 
  2.  * Base64Helper 
  3.  *  
  4.  * @author wenfengzhuo 
  5.  */  
  6. public class Base64Helper {  
  7.      public static String encode(byte[] byteArray) {  
  8.             sun.misc.BASE64Encoder base64Encoder = new sun.misc.BASE64Encoder();  
  9.             return base64Encoder.encode(byteArray);  
  10.         }  
  11.   
  12.         public static byte[] decode(String base64EncodedString) {  
  13.             sun.misc.BASE64Decoder base64Decoder = new sun.misc.BASE64Decoder();  
  14.             try {  
  15.                 return base64Decoder.decodeBuffer(base64EncodedString);  
  16.             } catch (IOException e) {  
  17.                 return null;  
  18.             }  
  19.         }  
  20.   
  21. }  
B) 加密

[java]  view plain copy print ?
  1. /** 
  2.      * 加密 
  3.      *  
  4.      * @author wenfengzhuo 
  5.      */  
  6.     public static String encrypt(String text) throws Exception  
  7.     {  
  8.         String encryptedText="";  
  9.         Cipher cipher=Cipher.getInstance("RSA");  
  10.           
  11.         cipher.init(Cipher.ENCRYPT_MODE,getPublicKey() );  
  12.         byte[] dataReturn=null;  
  13.         byte[] data=text.getBytes("UTF-8");  
  14.         if(data.length<=100)  
  15.         {  
  16.             dataReturn=cipher.doFinal(data);  
  17.         }  
  18.         else  
  19.         {  
  20.            for (int i = 0; i < data.length; i += 100) {    
  21.                byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i,    
  22.                        i + 100));    
  23.                dataReturn = ArrayUtils.addAll(dataReturn, doFinal);    
  24.            }   
  25.         }  
  26.         encryptedText=(new BASE64Encoder()).encode(dataReturn);  
  27.         return encryptedText;  
  28.           
  29.     }  

C) 解密 

[java]  view plain copy print ?
  1. /** 
  2.      * 解密 
  3.      *  
  4.      * @author wenfengzhuo 
  5.      */  
  6.     public static String decrypt(String text ) throws Exception  
  7.     {  
  8.         Cipher cipher=Cipher.getInstance("RSA");  
  9.         cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());  
  10.         StringBuilder sb = new StringBuilder();    
  11.         byte[] data=new BASE64Decoder().decodeBuffer(text);  
  12.         if(data.length<=128)  
  13.         {  
  14.             sb.append(new String(cipher.doFinal(data)));  
  15.         }  
  16.         else  
  17.         {  
  18.             for (int i = 0; i < data.length; i += 128) {   
  19.                 byte[] piece=ArrayUtils.subarray(data, i, i + 128);  
  20.                 byte[] doFinal = cipher.doFinal(piece);    
  21.                 sb.append(new String(doFinal,"UTF-8"));    
  22.             }  
  23.         }  
  24.         return sb.toString();  
  25.     }  


注:转载请注明出处,做一个文明的IT人。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值