C#與Java的RSA(3)

weixin_34319999發表於2017-11-08

原創文章,轉載請註明出處 http://boytnt.blog.51cto.com/966121/1351207


在上一篇文章裡,我們已經成功的解析出了公鑰加密至關重要的modulus和publicExponent,勝利在望,心急的同學肯定要開始這麼寫了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/*********** C#程式碼 ***********/
                                                      
//設定RSA引數,用於指定modulus和exponent
var parameter = new RSAParameters();
parameter.Modulus = modulusBytes;       //modulusBytes是轉化後的byte[]
parameter.Exponent = exponentBytes;
                                                      
//加密
var rsa = RSACryptoServiceProvider.Create("RSA");
rsa.ImportParameters(parameter);
byte[] result = rsa.EncryptValue(Encoding.UTF8.GetBytes("PASSWORD"));
                                                    
//把密文轉化為HEX形式
string resultHex = BitConverter.ToString(result).Replace("-""");


對不起要無情的打擊你了,這麼做行不通,因為C#的RSA加密不是標準RSA,為了加強安全性,它強制使用了PKCS1填充(或OAEP填充),導致密文在Java端解密失敗,因為Java端用的是無填充模式(RSA/ECB/NoPadding)。


怎麼辦? 自己實現標準RSA加密吧。modulus和publicExponent都有了,再加上密碼,都轉化成大整數,做一個ModPow操作,就得到結果密文了。正好.NetFramework從4.0起在System.Numerics名稱空間下直接提供BigInterger類了,上手試試:

1
2
3
4
5
6
7
8
9
10
11
12
/*********** C#程式碼 ***********/
                                             
//把3個變數轉化為System.Numerics.BigInteger
var modulus = new BigInteger(modulusBytes);
var exponent = new BigInteger(exponentBytes);
var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD"));
                                             
//做ModPow運算得到密文,也是BigInteger
var result = BigInteger.ModPow(data, exponent, modulus);
                                             
//把密文BigInteger對應的byte[]轉化為HEX形式
string resultHex = BitConverter.ToString(result.ToByteArray()).Replace("-""");


OH MY GOD!!!  還是不行,為什麼?


是位元組序在搗亂,我們上面分析ASN.1結構時得到的所有資料都是大位元組序的,而System.Numerics.BigInteger是小位元組序的,需要轉化之後才能使用。這點很煩人,要轉來轉去的。

1
2
3
4
5
6
7
8
9
10
11
12
/*********** C#程式碼 ***********/
                                             
//把3個變數轉化為System.Numerics.BigInteger
var modulus = new BigInteger(modulusBytes.Reverse().ToArray());
var exponent = new BigInteger(exponentBytes.Reverse().ToArray());
var data = new BigInteger(Encoding.UTF8.GetBytes("PASSWORD").Reverse().ToArray());
                                             
//做ModPow運算得到密文,也是BigInteger
var result = BigInteger.ModPow(d, e, m);
                                             
//把密文BigInteger對應的byte[]轉化為HEX形式
string resultHex = BitConverter.ToString(result.ToByteArray().Reverse().ToArray()).Replace("-""");


再試一下,這下終於OK了。


問題完美解決了嗎? 很遺憾,沒有,因為System.Numerics.BigInteger是4.0以上版本才提供的,低於此版本的只好去使用第三方的實現,比如這個:http://www.codeproject.com/Articles/2728/Csharp-BigInteger-Class,但要注意以下2點:


1、它預設只支援560bit,程式碼最開頭有寫:private const int maxLength = 70;

把maxLength改成256才能支援2048位的RSA運算了。

2、它是大位元組序的,在使用時不用反轉modulus和publicExponent對應的byte[]。

3、我們得到的modulus是257個位元組,需要去掉首位元組的0,用剩下的256個位元組例項化BigInteger。



好,至此問題算是告一段落了,實測通過。以後想起來什麼再繼續補充吧。

我沒有提供完整解決方案的程式碼,只給出了部分程式碼片斷,希望對此問題有興趣的同學把重點放在理解過程上,而不是僅僅搬程式碼。





     本文轉自 BoyTNT 51CTO部落格,原文連結:http://blog.51cto.com/boytnt/1351207,如需轉載請自行聯絡原作者

相關文章