2011-10-21 65 views
9

Tôi biết câu trả lời chính mà tôi có thể nhận được là tại sao bạn muốn làm điều đó?Sử dụng Khóa công cộng RSA để giải mã một chuỗi đã được mã hóa bằng Khóa riêng RSA

Thật không may mặc dù các cuộc biểu tình của tôi, tôi phải làm điều đó, mặc dù tôi biết nó có ý nghĩa rất ít.

Tôi có các hàm được viết bằng .Net để giải mã bằng khóa riêng, mã hóa bằng khóa công khai. Tôi cũng RSA ký và xác minh và có một sự hiểu biết hợp lý về cách làm việc này tất cả tôi nghĩ.

Tôi hiện đang được gửi một giá trị được mã hóa RSA bằng khóa riêng tư mà tôi cho rằng sẽ lấy được giá trị có thể sử dụng bằng cách giải mã bằng khóa công khai.

Tôi dường như không thể tìm ra cách thực hiện việc này. Tôi có phải là một thằng ngốc không? Đây có phải là điều bình thường để làm không?

Tôi được người gửi cho tôi biết rằng giá trị này không có vấn đề gì trong PHP. Tôi chưa biết và chưa sử dụng PHP. Tôi không thể tìm thấy thư viện để thực hiện nó bằng bất kỳ ngôn ngữ chính nào mà tôi biết, ví dụ: C++, Java, C#. Máy chủ tôi đang sử dụng .Net.

Tôi hy vọng ai đó có thể giúp tôi.

Sẽ là tuyệt vời nếu có một số giải pháp hợp lý bên cạnh việc cầu xin họ thay đổi những gì họ đang làm.

Đây là phương pháp của tôi (cập nhật từ một xấu trước đây của tôi như đã chỉ ra bởi Iridium) nhưng khi tôi cố gắng để giải mã giá trị tôi nhận được một ngoại lệ

"Xảy ra lỗi lầm trong khi giải mã đệm OAEP."

Nếu tôi sử dụng rsa.Decrypt (byte, false) Tôi nhận được ngoại lệ khóa không hợp lệ.

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; 
    } 

Hai phương pháp trên InitRSAProvider và LoadRsaPublicKey đã được đưa ra hướng dẫn để cho phép các khóa PEM sử dụng với chuỗi .Net.

+0

Nói gì? Đó là chính xác những gì các thuật toán được cho là phải làm. Giải mã bằng khóa riêng tư được mã hóa bằng khóa công khai tương ứng. Hoặc là bạn đang bối rối thời gian lớn hay tôi! C# hỗ trợ hoàn toàn RSA :) –

+0

Xin lỗi. Đã chỉnh sửa câu hỏi của tôi. Tôi đã viết nó hoàn toàn sai. Brain bắt đầu tranh giành một chút vào thứ Sáu. –

+0

Bạn không thể tìm ra điều gì? Bạn nói rằng bạn đã có chức năng để giải mã bằng cách sử dụng một khóa công khai. – James

Trả lời

12

Sau khi xem xét một số thông tin về chế độ mã hóa RSA, có vẻ như PKCS # 1 v1.5 (mà bạn đang sử dụng, bởi vì bạn đang gọi điện thoại Decrypt(..., false))

" ... có thể hoạt động trên các thông điệp có độ dài lên đến k - 11 octet (k là độ dài octet của RSA modulus) "

(RFC 3447, nhấn mạnh mỏ).

Dựa trên thông báo lỗi, cho biết khóa của bạn là 128 byte, điều đó có nghĩa là bạn không thể thực hiện thao tác RSA (en | de) bằng PKCS # 1 v1.5 trên thư có hơn 128 - 11 = 117 byte.

Thay vì mã hóa thư của bạn trực tiếp bằng RSA, bạn nên sử dụng thuật toán đối xứng để mã hóa nội dung thư và chỉ mã hóa khóa mã hóa đối xứng bằng RSA. Chỉ khi thông điệp của bạn ngắn gọn (tức là dưới 117 byte cho kích thước khóa của bạn), bạn nên xem xét mã hóa tin nhắn trực tiếp với RSA.

Tôi đã thêm những điều sau đây, giả định rằng đầu vào của bạn là Base64 mã hóa như bạn chỉ rõ trong bình luận của bạn dưới đây:

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

Cảm ơn bạn đã trả lời. Tôi đã đọc RFC 3447 cả ngày cố gắng tìm một giải pháp. Vấn đề chính là tôi không kiểm soát được giá trị được mã hóa như thế nào. Về cơ bản tôi được cung cấp một giá trị tức là giá trị = BASE64 [RSA_PRIVATE_ENCRYPT (some_text)] Nó đã được để lại cho tôi để tìm ra cách giải mã và sử dụng giá trị này. Tôi đã thử rất nhiều thứ nhưng không có niềm vui. Tôi đã yêu cầu một ví dụ làm việc để giải mã từ người gửi cho tôi giá trị. Nếu tôi nhận được nó, tôi sẽ đăng nó ở đây. –

+0

Đó không phải là những gì đoạn mã của bạn đề xuất, vì bạn không có giải mã Base64. (Bạn không nên sử dụng Encoding.ASCII cho mục đích này, vì nó chỉ là 7-bit). Nếu đầu vào của bạn là Base64, bạn nên sử dụng Convert.FromBase64String (dataEncrypted) để nhận mảng byte để sử dụng với các hàm RSA. – Iridium

+0

Trong khi câu trả lời ban đầu của tôi vẫn áp dụng, tôi đã thêm một số mã vào câu trả lời có thể khắc phục sự cố của bạn, dựa trên giả định rằng đầu vào trên thực tế được mã hóa Base64 như bạn đã đề cập trong nhận xét ở trên. – Iridium

13

RSA được tích hợp vào .NET: System.Security.Cryptography.RSA.

Mã hóa bằng khóa công khai và giải mã bằng khóa cá nhân là một trong những điều phổ biến nhất mọi người làm với các thuật toán không đối xứng, cho phép bất kỳ ai gửi cho bạn thứ gì đó an toàn.

Nếu bạn thực hiện theo cách khác: mã hóa bằng khóa cá nhân và giải mã bằng khóa công khai, nó sẽ chứng minh rằng thông điệp được gửi bởi người giữ khóa riêng. Nhưng bởi vì bất cứ ai có lẽ có thể nắm giữ khóa công khai, mọi người không có xu hướng mã hóa toàn bộ thông điệp, thay vào đó họ chỉ cần ký một băm dữ liệu bằng khóa riêng. Do đó, RSACryptoServiceProvider có các phương thức Sign__Verify__ để thực hiện điều đó.

Tuy nhiên, có Encrypt/Decrypt các phương pháp nếu đối tác của bạn khẳng định.

Nói rằng, tôi đã tìm thấy các lớp mã hóa của Microsoft hơi phức tạp để giải quyết và thiếu trong một số khu vực nhất định và thích nhiều hơn là Bouncy Castle libraries.

+0

Xin lỗi tôi là một thằng ngốc. Tôi nhận được câu hỏi của tôi hoàn toàn ngược. Tôi đã chỉnh sửa nó để hiển thị những gì tôi thực sự có ý nghĩa. –

+1

Suy nghĩ như vậy! Đã cập nhật câu trả lời. –

+0

Cảm ơn rất nhiều vì câu trả lời của bạn. Tôi không thể có được phương pháp giải mã để làm việc trong trường hợp này. Tôi đã cập nhật câu hỏi của mình để cho biết nó đang diễn ra ở đâu. Tôi có thể làm điều gì đó ngớ ngẩn và sẽ đánh dấu câu trả lời này ngay khi tôi tìm ra nó là gì. –

6

RSA là không có nghĩa là để mã hóa dữ liệu tùy ý, thậm chí ít hơn tùy ý chiều dài dữ liệu (như @ Iridium đã nói với bạn). Giới hạn phụ thuộc vào padding được sử dụng và sử dụng padding là rất quan trọng (đủ để MS không cho phép bạn gọi trực tiếp EncryptValueDecryptValue).

phải cách để làm điều này là mã hóa chuỗi của bạn bằng mật mã đối xứng (như AES), sau đó mã hóa khóa bí mật bằng khóa công cộng RSA.

Bên kia sẽ có thể giải mã khóa bí mật (AES) bằng khóa riêng RSA. Sau đó, sử dụng phím giải mã chuỗi của bạn.

Tôi có một cũ (nhưng vẫn cập nhật) blog entry về chủ đề mà bao gồm mã nguồn (C#).

+0

Cảm ơn bạn đã trả lời. Tôi hoàn toàn đồng ý với bạn. Vấn đề là tôi không mã hóa giá trị. Tôi có một khóa riêng và một khóa công khai. Tôi đang được gửi một giá trị đã được mã hóa bằng cách sử dụng khóa riêng và bây giờ tôi phải tìm ra cách giải mã nó. Nó có thể không có ý nghĩa và có thể không có một giải pháp đơn giản nhưng tôi không phải là rất có kinh nghiệm với điều này vì vậy tôi đánh giá cao tất cả các ý kiến ​​và góp ý. –

+1

Cũng giống như blog của bạn. –

+0

Nếu bạn bị kẹt ở đầu ** giải mã ** thì bạn không có lựa chọn nào (an toàn hay không) so với thực hiện các thao tác ngược lại để lấy lại dữ liệu. Hy vọng rằng nó sẽ không yêu cầu bạn sử dụng 'DecryptValue' (nó không nên nếu khác, mã hóa, bên sử dụng. NET) nhưng ngay cả sau đó có những giải pháp cho rằng :) Hãy cho chúng tôi biết! – poupou

Các vấn đề liên quan