2011-10-21 310 views
9

我知道我可能得到的主要答案是爲什麼你會想要這樣做?使用RSA公鑰解密使用RSA私鑰加密的字符串

不幸的是,儘管我抗議,但我必須這麼做,儘管我知道這沒什麼意義。

我用.Net編寫函數來解密使用私鑰,使用公鑰加密。 我也RSA簽署和驗證,並有一個合理的理解這一切,我認爲如何工作。

我現在正在發送的值是使用它我應該使用公共密鑰來解密以獲得一個可用值私鑰加密的RSA。

我似乎無法弄清楚如何做到這一點。我是個白癡嗎?這是一個正常的事情嗎?

發送給我的人告訴我,這在PHP中沒有問題。我不知道和尚未使用PHP。我無法找到一個圖書館來使用我所知道的任何主要語言,即C++,Java,C#。我正在使用的服務器使用.Net。

我希望有人能夠幫助我。

,如果有某種合理的解決方案,除了乞求他們改變他們在做什麼,這將是巨大的。

這是我的方法(從我以前的壞一個更新銥星指出的),但是當我嘗試解密值我得到一個異常

「而解碼OAEP填充時發生錯誤。」

如果我使用rsa.Decrypt(bytes,false),我得到一個壞鍵異常。

public static string DecryptUsingPublic(string dataEncrypted, string publicKey) 
    { 
     if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncrypted); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, true); 

      ArrayList arrayList = new ArrayList(); 
      arrayList.AddRange(decryptedBytes); 

      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 

    private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile) 
    { 
     RSAParameters RSAKeyInfo = new RSAParameters(); 
     byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile); 
     byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; 
     byte[] seq = new byte[15]; 
     // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ 
     MemoryStream mem = new MemoryStream(pubkey); 
     BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading 
     byte bt = 0; 
     ushort twobytes = 0; 

     try 
     { 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      seq = binr.ReadBytes(15);  //read the Sequence OID 
      if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8203) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      bt = binr.ReadByte(); 
      if (bt != 0x00)  //expect null byte next 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) 
       binr.ReadByte(); //advance 1 byte 
      else if (twobytes == 0x8230) 
       binr.ReadInt16(); //advance 2 bytes 
      else 
       return RSAKeyInfo; 

      twobytes = binr.ReadUInt16(); 
      byte lowbyte = 0x00; 
      byte highbyte = 0x00; 

      if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) 
       lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus 
      else if (twobytes == 0x8202) 
      { 
       highbyte = binr.ReadByte(); //advance 2 bytes 
       lowbyte = binr.ReadByte(); 
      } 
      else 
       return RSAKeyInfo; 
      byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order 
      int modsize = BitConverter.ToInt32(modint, 0); 

      byte firstbyte = binr.ReadByte(); 
      binr.BaseStream.Seek(-1, SeekOrigin.Current); 

      if (firstbyte == 0x00) 
      { //if first byte (highest order) of modulus is zero, don't include it 
       binr.ReadByte(); //skip this null byte 
       modsize -= 1; //reduce modulus buffer size by 1 
      } 

      byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes 

      if (binr.ReadByte() != 0x02)   //expect an Integer for the exponent data 
       return RSAKeyInfo; 
      int expbytes = (int)binr.ReadByte();  // should only need one byte for actual exponent data (for all useful values) 
      byte[] exponent = binr.ReadBytes(expbytes); 


      RSAKeyInfo.Modulus = modulus; 
      RSAKeyInfo.Exponent = exponent; 

      return RSAKeyInfo; 
     } 
     catch (Exception) 
     { 
      return RSAKeyInfo; 
     } 

     finally { binr.Close(); } 
     //return RSAparams; 

    } 

private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam) 
    { 
     // 
     // Initailize the CSP 
     // Supresses creation of a new key 
     // 
     CspParameters csp = new CspParameters(); 
     //csp.KeyContainerName = "RSA Test (OK to Delete)"; 

     const int PROV_RSA_FULL = 1; 
     csp.ProviderType = PROV_RSA_FULL; 

     const int AT_KEYEXCHANGE = 1; 
     // const int AT_SIGNATURE = 2; 
     csp.KeyNumber = AT_KEYEXCHANGE; 
     // 
     // Initialize the Provider 
     // 
     RSACryptoServiceProvider rsa = 
      new RSACryptoServiceProvider(csp); 
     rsa.PersistKeyInCsp = false; 

     // 
     // The moment of truth... 
     // 
     rsa.ImportParameters(rsaParam); 
     return rsa; 
    } 

    private static int GetIntegerSize(BinaryReader binr) 
    { 
     byte bt = 0; 
     byte lowbyte = 0x00; 
     byte highbyte = 0x00; 
     int count = 0; 
     bt = binr.ReadByte(); 
     if (bt != 0x02)  //expect integer 
      return 0; 
     bt = binr.ReadByte(); 

     if (bt == 0x81) 
      count = binr.ReadByte(); // data size in next byte 
     else 
      if (bt == 0x82) 
      { 
       highbyte = binr.ReadByte(); // data size in next 2 bytes 
       lowbyte = binr.ReadByte(); 
       byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; 
       count = BitConverter.ToInt32(modint, 0); 
      } 
      else 
      { 
       count = bt;  // we already have the data size 
      } 

     while (binr.ReadByte() == 0x00) 
     { //remove high order zeros in data 
      count -= 1; 
     } 
     binr.BaseStream.Seek(-1, SeekOrigin.Current);  //last ReadByte wasn't a removed zero, so back up a byte 
     return count; 
    } 

    private static bool CompareBytearrays(byte[] a, byte[] b) 
    { 
     if (a.Length != b.Length) 
      return false; 
     int i = 0; 
     foreach (byte c in a) 
     { 
      if (c != b[i]) 
       return false; 
      i++; 
     } 
     return true; 
    } 

兩種方法上述InitRSAProvider和LoadRsaPublicKey被得到了教程以允許PEM鍵字字符串與.net使用。

+0

說什麼?這正是算法應該做的。使用私鑰解密使用相應公鑰加密的內容。無論你是困惑的大時間還是我! C#完全支持RSA :) –

+0

對不起。編輯我的問題。我完全寫錯了。星期五大腦開始爭吵。 –

+0

你不能弄清楚什麼?你說你已經有了使用公鑰解密的功能。 – James

回答

12

查看了一些有關RSA加密模式的信息之後,就會發現PKCS#1 v1。5(你使用它,因爲你打電話Decrypt(..., false)

」 ......可以在長度的消息多達K操作 - 11個字節(k是RSA的八位字節長度模量)「

(RFC 3447,強調我的)。

根據錯誤消息(表明您的密鑰是128字節),這意味着您無法使用PKCS#1 v1.5在超過128 - 11的消息上執行RSA(en | de)加密= 117個字節。

不應使用RSA直接對消息進行加密,而應使用對稱算法來加密消息正文,並使用RSA僅對對稱加密密鑰進行加密。只有當您的郵件相當短(即您的密鑰大小低於117字節)時,才應考慮直接使用RSA加密郵件。

我已經添加了以下假設你輸入Base64編碼如您在下面的評論指出:

public string DecryptUsingPublic(string dataEncryptedBase64, string publicKey) 
    { 
     if (dataEncryptedBase64 == null) throw new ArgumentNullException("dataEncryptedBase64"); 
     if (publicKey == null) throw new ArgumentNullException("publicKey"); 
     try 
     { 
      RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false); 
      RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey); 

      byte[] bytes = Convert.FromBase64String(dataEncryptedBase64); 
      byte[] decryptedBytes = rsa.Decrypt(bytes, false); 

      // I assume here that the decrypted data is intended to be a 
      // human-readable string, and that it was UTF8 encoded. 
      return Encoding.UTF8.GetString(decryptedBytes); 
     } 
     catch 
     { 
      return null; 
     } 
    } 
+0

感謝您的回覆。我一整天都在閱讀RFC 3447,試圖找到解決方案。主要問題是我無法控制數值的加密方式。基本上我給了一個值,即值= BASE64 [RSA_PRIVATE_ENCRYPT(some_text)]我已經知道如何解密和使用這個值。我嘗試過很多東西,但沒有快樂。我已經要求爲發送給我的人解密的一個工作示例。如果我得到它,我會在這裏發佈。 –

+0

這不是你的代碼片斷所暗示的,因爲你沒有Base64解碼。 (你不應該爲此使用Encoding.ASCII,因爲它只有7位)。如果你的輸入是Base64,你應該使用Convert.FromBase64String(dataEncrypted)來獲得一個字節數組,以便與RSA函數一起使用。 – Iridium

+0

儘管我的原始答案仍然適用,但我已經在我的答案中添加了一些代碼,可以解決您的問題,這是基於假設輸入實際上是Base64編碼,就像您在上面的註釋中提到的那樣。 – Iridium

13

RSA內置於.NET:System.Security.Cryptography.RSA

使用公共密鑰加密和用私鑰解密是最常見的人們使用非對稱算法之一,它允許任何人安全地發送你的東西。

如果你做它的其他方式:使用加密私鑰,並用公鑰則證明了該消息是由私有密鑰的所有者發送的解密。但是因爲任何人都可以獲得公鑰,所以人們不傾向於加密整個消息,而只是使用私鑰對數據進行哈希簽名。因此RSACryptoServiceProviderSign__Verify__方法來做到這一點。

不過,也有Encrypt/Decrypt方法,如果你的伴侶堅持。

話說,我已經找到了微軟加密類有點棘手,處理,並在某些領域缺乏,更喜歡Bouncy Castle libraries

+0

對不起,我是個白癡。我完全倒退了我的問題。我已經編輯它來顯示我的意思。 –

+1

這麼想!更新了答案。 –

+0

非常感謝您的回答。在這種情況下,我似乎無法獲得解密方法。我已更新我的問題以顯示出錯的地方。我可能會做一些愚蠢的事情,只要我弄清楚它是什麼,就會將其標記爲答案。 –

6

RSA是意味着加密任意數據,甚至更少的任意數據長度(如@銥星已經告訴你了)。限制取決於使用的填充,並且使用填充很重要(足以使MS不會讓您直接撥打EncryptValueDecryptValue)。

right這樣做的方法是使用對稱密碼(如AES)對您的字符串進行加密,然後使用RSA公鑰對密鑰進行加密。

另一方將能夠使用RSA私鑰解密祕密(AES)密鑰。然後用密鑰解密你的字符串。

我有一箇舊的(但仍然是最新的)blog entry關於包括源代碼(C#)的主題。

+0

感謝您的迴應。我完全同意你的看法。問題是我沒有加密值。我有一個私鑰和一個公鑰。我正在發送一個使用私鑰加密的值,現在我必須弄清楚如何解密它。這可能沒有道理,並且可能沒有簡單的解決方案,但我對此不是很有經驗,所以我非常感謝所有意見和建議。 –

+1

喜歡你的博客的方式。 –

+0

如果你堅持**解密**結束,那麼你沒有選擇(安全或沒有),而不是做反向操作來取回數據。希望它不會要求你使用'DecryptValue'(它不應該如果其他的,加密,派對使用.NET),但即使如此,有解決方案:)讓我們知道! – poupou