2013-06-12 319 views
2

這是一個懸而未決的問題在這裏的副本:Using an RSA Public Key to decrypt a string that was encrypted using RSA Private Key使用RSA公鑰來解密使用RSA被加密的字符串私鑰

你可以看到筆者發現使用一些代碼的解決方案從這裏: http://www.codeproject.com/KB/security/PrivateEncryption.aspx

使用該鏈接的代碼看起來很有希望。唯一缺少的是填充。我通常使用PKCS1.5填充,這是OpenSSL RSA的默認填充。

我知道這個問題的答案非常接近。我知道唯一阻止解密的是加密的openssl密文上的pkcs1.5填充。

我很驚訝地發現這個主題上的信息很少,因爲在很多情況下你需要一個服務器來加密某些東西,簽署某些東西等等,並且讓客戶端應用程序驗證,解密等等公鑰。

我也廣泛嘗試使用RSACryptoServiceProvider來驗證使用OpenSSL進行加密所產生的哈希結果。例如,我會使用明文的SHA256散列進行私鑰加密,然後嘗試對該簽名執行RSACryptoServiceProvider驗證,但它不起作用。我認爲MS做這件事的方式並不標準,可能還有特殊的定製。

所以,另一種方法是這個問題,它只是採用私鑰加密的密文並使用C#解密它,從而驗證其真實性。哈希可以合併爲一個簡單的簽名驗證系統,用於由服務器簽名並在客戶端進行驗證的數據對象。

我已經瀏覽了PKCS1 RFC,OpenSSL rsa源代碼和其他項目,但是在做RSA解密時,我無法得到關於如何解釋PKCS1填充的明確答案。我無法找到他們處理PKCS1填充的OpenSSL源代碼的位置,否則,我現在可能會有答案。另外,這是我的第一個問題,我知道它是一個未答覆的問題的重複,所以,該怎麼辦?我也搜索了這個,並沒有發現任何東西。

我不明白的另一件事是爲什麼我的解密方法不起作用。由於填充在解密後被移除,我的解密數據應該類似於明文,並且它甚至不是很接近。所以,我幾乎肯定pkcs1填充意味着其他事情正在發生,特別是對於密文,這意味着密文在解密之前必須被預處理以去除填充元素。

也許只是過濾密文去除填充元素是這裏最簡單的解決方案......

這裏是我的解密方法:

public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData) 
    { 
     if (cipherData == null) 
      throw new ArgumentNullException("cipherData"); 

     BigInteger numEncData = new BigInteger(cipherData); 

     RSAParameters rsaParams = rsa.ExportParameters(false); 
     BigInteger Exponent = GetBig(rsaParams.Exponent); 
     BigInteger Modulus = GetBig(rsaParams.Modulus); 

     BigInteger decData = BigInteger.ModPow(numEncData, Exponent, Modulus); 

     byte[] data = decData.ToByteArray(); 
     byte[] result = new byte[data.Length - 1]; 
     Array.Copy(data, result, result.Length); 
     result = RemovePadding(result); 
     Array.Reverse(result); 
     return result; 
    } 

    private static byte[] RemovePadding(byte[] data) 
    { 
     byte[] results = new byte[data.Length - 4]; 
     Array.Copy(data, results, results.Length); 
     return results; 
    } 
+0

加密代碼在哪裏?有*兩種* PKCS#1版本1.5填充:塊類型(BT)1和塊類型2填充。 BT 1用於你正在做的RSA簽名,BT 2用於RSA加密。 –

回答

1

的問題是不是與填充。事實上,從解密的密文中移除填充值實際上非常簡單。問題是與在此位置的軟件: 你可以看到筆者發現利用這裏的一些代碼的解決方案:http://www.codeproject.com/KB/security/PrivateEncryption.aspx

以及與Microsoft的實現System.Numeric的這根本無法處理更大的整數...

爲了解決這個問題,我查看了codeproject站點上的以前版本的代碼,並以此PublicDecrypt方法結束。

public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData) 
    { 
     if (cipherData == null) 
      throw new ArgumentNullException("cipherData"); 

     BigInteger numEncData = new BigInteger(cipherData); 
     RSAParameters rsaParams = rsa.ExportParameters(false); 
     BigInteger Exponent = new BigInteger(rsaParams.Exponent); 
     BigInteger Modulus = new BigInteger(rsaParams.Modulus); 

     BigInteger decData2 = numEncData.modPow(Exponent, Modulus); 
     byte[] data = decData2.getBytes(); 
     bool first = false; 
     List<byte> bl = new List<byte>(); 
     for (int i = 0; i < data.Length; ++i) 
     { 
      if (!first && data[i] == 0x00) 
      { 
       first = true; 
      } 
      else if (first) 
      { 
       if (data[i] == 0x00) 
       { 
        return bl.ToArray(); 
       } 
       bl.Add(data[i]); 
      } 
     } 
     if (bl.Count > 0) 
      return bl.ToArray(); 
     return new byte[0]; 
    } 

這將完全解密使用rsautl實用程序通過OpenSSL的創建的密文,或Perl的隱窩:: OpenSSL的:: RSA private_encrypt方法。

另一個重大變化是丟棄了Microsoft BitInteger庫,它根本無法工作。我最終使用了代碼項目文章中提到的一個,並在此處找到: http://www.codeproject.com/Articles/2728/C-BigInteger-Class

此處的關鍵是將庫中的maxintsize設置爲基於密鑰大小有多大的值使用。對於4096位,500的值工作得很好(近似模數的長度)。

這裏是調用方法:

  var encmsg3 = "JIA7qtOrbBthptILxnurAeiQM3JzSoi5WiPCpZrIIqURKfVQMN1BrondF9kyNzbjTs1DaEKEuMBVwKExZe22yCvXXpm8pwcEGc9EHcVK2MPqNo89tIF8LJcaDqBMwLvxdaa/QgebtpmOVN/TIWfuiV8KR+Wn07KwsgV+3SALbNgbOIR3RtCx3IiQ3tZzybJb08+ZKwJEOT011uwvvGmtJskQgq9PC8kr1RPXMgaP6xMX7PHFJ8ORhkuWOFfCh+R4NhY1cItVhnRewKpIC2qVlpzUYRAgKIKdCXuZDqUQdIponR29eTovvLb0DvKQCLTf9WI1SzUm6pKRn0vLsQL7L3UYHWl43ISrTpDdp+3oclhgRF3uITR4WCvoljephbGc6Gelk5z3Vi6lN0oQaazJ7zIen+a/Ts7ZX3KKlwPl4/lAFRjdjoqu7u4IAK7O7u1Jf2xDiGw18C/eGt8UHl09zU4qQf9/u+7gtJ+10z2NERlLSaCDjVqslwmmxu81pG2gCv8LfpR4JlPaFfBZMGfGBihyGryWhJwizUXXo8wgdoYbHRXe8/gL19qro0ea5pA9aAhDjTpX1Zzbwu2rUU7j6wtwQtUDJOGXXCw1VOHsx6WXeW196RkqG72ucVIaSAlx5TFJv8cnj6werEx1Ung5456gth3gj19zHc8E/Mwcpsk="; 
      byte[] enc = Convert.FromBase64String(encmsg3); 
      var dec = rsa2.PublicDecryption(enc); 
      Debug.Print("PLAINTEXT: " + Encoding.UTF8.GetString(dec)); 

唯一的最後一件事,有人會需要完全複製此會得到私鑰到OpenSSL的格式,使他們能夠通過私鑰和公鑰來回在openssl和C#之間。

我使用openssl.net,並創建了一個RSA實例,並使用bignumbers設置所有變量。下面是該代碼:

 RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
     rsa.FromXmlString(Properties.Resources.RSAParameters); 

     RSAParameters par = rsa.ExportParameters(true); // export the private key 


     using (OpenSSL.Crypto.RSA rsaos = new OpenSSL.Crypto.RSA()) 
     using (BigNumber bnmod = BigNumber.FromArray(par.Modulus)) 
     using (BigNumber bnexp = BigNumber.FromArray(par.Exponent)) 
     using (BigNumber bnD = BigNumber.FromArray(par.D)) 
     using (BigNumber bnP = BigNumber.FromArray(par.P)) 
     using (BigNumber bnQ = BigNumber.FromArray(par.Q)) 
     using (BigNumber bnDmodP = BigNumber.FromArray(par.DP)) 
     using (BigNumber bnDmodQ = BigNumber.FromArray(par.DQ)) 
     using (BigNumber bnInverse = BigNumber.FromArray(par.InverseQ)) 
     { 
      rsaos.PublicExponent = bnexp; 
      rsaos.PublicModulus = bnmod; 

      rsaos.IQmodP = bnInverse; 
      rsaos.DmodP1 = bnDmodP; 
      rsaos.DmodQ1 = bnDmodQ; 
      rsaos.SecretPrimeFactorP = bnP; 
      rsaos.SecretPrimeFactorQ = bnQ; 
      rsaos.PrivateExponent = bnD; 
      string privatekey = rsaos.PrivateKeyAsPEM; 
      string publickey = rsaos.PublicKeyAsPEM 
     } 

這樣,您可以輕鬆地創建RSA密鑰,出口一切的OpenSSL和加密/解密你想在合理範圍內任何東西。處理私鑰加密和公鑰解密足夠了。

很酷。

-1

PublicDecryption函數中有一行存在問題: BigInteger numEncData = new BigInteger(cipherData);

它應該是: BigInteger numEncData = GetBig(cipherData);

該行也應該刪除: Array.Reverse(result);

您可能會遇到一些填充問題,但如果您可以正確獲取數據,則應該很容易對其進行糾正。