.NET和java的RSA互通,僅此而已
在開始這篇文章之前,先請讀者朋友閱讀老唐的這兩篇文章:
和我的這篇文章
前面老唐的兩篇文章中提到,要想實現.NET和Java的RSA互通,只能拋棄.NET現有的加密項目中的BigInteger類(.NET Framework4中已增加了這個類的實現,在System.Numberic命名空間中),這個BigInteger類實際上就是仿照着java的BigInteger類來寫的。
利用這個類的確可以很好的實現RSA的加解密,比如,在.NET端,構建一個公鑰對應的BigIntegere、一個模對應的BigInteger n和一個明文對應的BigIntegerm,然後執行語句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
{
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);
}