NET和java的RSA互通,仅此而已

Posted 沈成才

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NET和java的RSA互通,仅此而已相关的知识,希望对你有一定的参考价值。

在开始这篇文章之前,先请读者朋友阅读老唐的这两篇文章:
1、Java与.Net环境下RSA加密解密交互不成功的问题解决
2、Java与.Net环境下RSA加密解密交互不成功的问题解决【续】
和这篇文章
3、.NET_RSA加密全接触(重、难点解析)
前面老唐的两篇文章中提到,要想实现.NET和Java的RSA互通,只能抛弃.NET现有的加密算法,而是利用http://www.codeproject.com/csharp/biginteger.asp项目中的BigInteger类(.NET Framework4中已增加了这个类的实现,在System.Numberic命名空间中),这个BigInteger类实际上就是仿照着java的BigInteger类来写的。
利用这个类的确可以很好的实现RSA的加解密,比如,在.NET端,构建一个公钥对应的BigInteger e、一个模对应的BigInteger n和一个明文对应的BigInteger m,然后执行语句BigInteger c=m.modPow(e,n),便可以实现加密操作,密文为c,这样的加密是标准加密,没有附加任何填充算法的加密。
老唐的文章中说,不能互通是因为加密标准不一样,导致一方加密而另一方不能解密,其实不然,.NET采用的加密标准是PKCS1Padding(或OAEPPadding——只支持XP以上版本),这也是我在前面一篇文章中提到的一种填充算法,而java同样支持这一填充标准,既然可以遵循统一的标准,那么.NET和java的RSA互通,无需添加任何新代码便可以轻松实现!
请看下面的示例(.NET端加密,Java端解密):
Java端代码:

import java.math.BigInteger;    
import java.util.Scanner;  
import java.security.KeyFactory;     
import java.security.PrivateKey;    
import java.security.KeyPair;  
import java.security.KeyPairGenerator;  
import java.security.PublicKey;  
import java.security.interfaces.RSAPrivateKey;  
import java.security.interfaces.RSAPublicKey;       
import java.security.spec.RSAPublicKeySpec;     
import javax.crypto.Cipher;  
import sun.misc.*;  
public class RsaKey       

    public static void main(String[] args) throws Exception   
        //生成公私钥对  
         KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");        
         keyPairGen.initialize(1024);        
         KeyPair keyPair = keyPairGen.generateKeyPair();       
         PublicKey publicKey = (RSAPublicKey) keyPair.getPublic();     
         PrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();       

         //将公钥和模进行Base64编码  
         KeyFactory keyFactory = KeyFactory.getInstance("RSA");     
         RSAPublicKeySpec publicSpec= keyFactory.getKeySpec(publicKey,RSAPublicKeySpec.class);  
         BigInteger modulus = publicSpec.getModulus();  
         BigInteger exponent=publicSpec.getPublicExponent();  
         byte[] ary_m=modulus.toByteArray();//注意:对公钥和模进行Base64编码时,不是对BigInteger对应的字符串编码,而是对其内部 的字节数组进行编码  
         byte[] ary_e=exponent.toByteArray();  
         String str_m;  
         String str_e;  
         if(ary_m[0]==0 && ary_m.length==129)//判断数组首元素是否为0,若是,则将其删除,保证模的位数是128  
           
              byte[] temp=new byte[ary_m.length-1];  
              for(int i=1;i<ary_m.length;i++)  
                
                   temp[i-1]=ary_m[i];  
                
              str_m=(new BASE64Encoder()).encodeBuffer(temp);  
           
         else  
           
              str_m=(new BASE64Encoder()).encodeBuffer(ary_m);  
           

        str_e=(new BASE64Encoder()).encodeBuffer(ary_e);  
        System.out.println("公钥为:"+str_e);  
        System.out.println("模为:"+str_m);  
        System.out.println("运行.NET程序,用所提供的公钥和模进行加密,然后将加密结果输入本程序进行解密:");  
        Scanner sc=new Scanner(System.in);  
        String str_en="";  
        String st="";  
        while(!(st=sc.nextLine()).equals(""))  
          
             str_en+=st;  
          
        byte[] ary_en=(new BASE64Decoder()).decodeBuffer(str_en);  
         //解密  
          //注意Cipher初始化时的参数“RSA/ECB/PKCS1Padding”,代表和.NET用相同的填充算法,如果是标准RSA加密,则参数为“RSA”  
          Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");     
         cipher.init(Cipher.DECRYPT_MODE, privateKey);     
         byte[] deBytes = cipher.doFinal(ary_en);     
         String s = new String(deBytes );  
         System.out.println("解密结果为:" + s);  
          
   

Java端演示截图

.NET端代码:

static void Main(string[] args)  
  
    try  
      
        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();  
        RSAParameters para = new RSAParameters();  
        //加密  
           Console.WriteLine("请输入公钥");  
        string publicKey = Console.ReadLine();  
        Console.WriteLine("请输入模:");  
        string modulus = Console.ReadLine();  
        while(true)  
          
            string s = Console.ReadLine();  
            if (s == "")  
              
                break;  
              
            else  
              
                modulus += s;  
              
          
        Console.WriteLine("请输入明文:");  
        string m = Console.ReadLine();  
        para.Exponent = Convert.FromBase64String(publicKey);  
        para.Modulus = Convert.FromBase64String(modulus);  
        rsa.ImportParameters(para);  
        byte[] enBytes = rsa.Encrypt(UTF8Encoding.UTF8.GetBytes(m),false);  
        Console.WriteLine("密文为:"+Convert.ToBase64String(enBytes));  
        Console.ReadLine();  
      
    catch(Exception  ex)  
      
        Console.WriteLine(ex.Message);  
        Console.ReadLine();  
      

.NET端演示截图:

接下来的示例是(java端加密,.NET端解密):
.net端代码:

static void Main(string[] args)  
        
            try  
              
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();  
                RSAParameters para = rsa.ExportParameters(true);  
                //加¨®密¨¹  
                Console.WriteLine("公?钥?为a:êo"+ Convert.ToBase64String(para.Exponent));  
                Console.WriteLine("模¡ê为a:êo" + Convert.ToBase64String(para.Modulus));  
                Console.WriteLine("请?输º?入¨?密¨¹文?");  
                string enStr = Console.ReadLine();  
                while(true)  
                  
                    string s = Console.ReadLine();  
                    if (s == "")  
                      
                        break;  
                      
                    else  
                      
                        enStr += s;  
                      
                  
                byte[] deBytes = rsa.Decrypt(Convert.FromBase64String(enStr),false);  
                Console.WriteLine("明¡Â文?为a:êo"+UTF8Encoding.UTF8.GetString(deBytes));  
                Console.ReadLine();  
              
            catch(Exception  ex)  
              
                Console.WriteLine(ex.Message);  
                Console.ReadLine();  
              
         

Java端代码:

public static void main(String[] args) throws Exception   
       Scanner sc=new Scanner(System.in);  

       //获取公钥、模及明文的字符串  
        System.out.println("请输入公钥:");  
       String str_exponent=sc.nextLine();  
       System.out.println("请输入模:");  
       String str_modulus="";  
       String st="";  
       while(!(st=sc.nextLine()).equals(""))  
         
            str_modulus+=st;  
         
       System.out.println("请输入明文:");  
       String str_m=sc.nextLine();  

       //创建公钥  
        byte[] ary_exponent=(new BASE64Decoder()).decodeBuffer(str_exponent);  
       byte[] ary_modulus=(new BASE64Decoder()).decodeBuffer(str_modulus);  
       //注意构造函数,调用时指明正负值,1代表正值,否则报错  
        BigInteger big_exponent = new BigInteger(1,ary_exponent);  
       BigInteger big_modulus = new BigInteger(1,ary_modulus);  
       RSAPublicKeySpec keyspec=new RSAPublicKeySpec(big_modulus,big_exponent);  
       KeyFactory keyfac=KeyFactory.getInstance("RSA");  
       PublicKey publicKey=keyfac.generatePublic(keyspec);  

        //进行加密  
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");     
       cipher.init(Cipher.ENCRYPT_MODE, publicKey);     
       byte[] enBytes = cipher.doFinal(str_m.getBytes());     
       String s = (new BASE64Encoder()).encodeBuffer(enBytes);  
       System.out.println("加密结果为:" + s);  
     

以上是关于NET和java的RSA互通,仅此而已的主要内容,如果未能解决你的问题,请参考以下文章

全面解决.Net与Java互通时的RSA加解密问题,使用PEM格式的密钥文件

java和php实现RSA加密互通-b

rsa互通密钥对生成及互通加解密(c#,java,php)

C# 与JAVA 的RSA 加密解密交互,互通,C#使用BouncyCastle来实现私钥加密,公钥解密的方法

.NET Core加解密实战系列之——RSA非对称加密算法

RSA密钥,JAVA与.NET之间转换