2011-08-23 106 views
2

我正在使用一個API,它指定某些文本必須通過「RSA/ECB/PKCS1Padding」進行編碼傳遞。公鑰以.PEM格式提供;我認爲我已經設法成功地轉換爲XML格式,現在我使用此C#代碼:使用RSA編碼一個字符串

RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(); 
    rsa1.FromXmlString(testpublickey); 

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); 
    byte[] textBytes = encoding.GetBytes(plainstring); 
    byte[] encryptedOutput = rsa1.Encrypt(textBytes, false); 
    string outputB64 = Convert.ToBase64String(encryptedOutput); 
    Console.WriteLine(outputB64); 
    return outputB64; 

然而,返回的字符串不匹配的預期 - 該API提供了以下以OpenSSL命令爲例:

openssl rsautl -encrypt -in PIN.txt -inkey public_key_for_pin.pem -pubin | openssl base64 > PIN.base64 

OpenSSL的輸出與我的代碼的輸出不匹配。任何人都可以看到我的代碼有什麼問題;我是否需要指定特定的填充,或者是否可能在從PEM到XML的翻譯中損壞了公鑰文件?

+0

你不設置填充的。你知道默認嗎? –

+0

@Henk Holterman:填充由'rsa1.Encrypt()'調用中的'false'參數指定。 'false'會得到PKCS#1塊類型2的填充。 –

回答

3

我這樣做用公鑰加密數據。

首先將PEM數據加載到X509Certificate2類中,它具有可以使用的導入方法。

然後我用下面的代碼:

using System.Security.Cryptography.X509Certificates; 
using System.Security.Cryptography.Pkcs; 
using System.Security.Cryptography; 

public static string EncryptString(string clearText, X509Certificate2 cert) 
{ 
    try 
    { 
     byte[] encodedCypher = EncryptData(Encoding.UTF8.GetBytes(clearText), cert); 
     string cipherText = Convert.ToBase64String(encodedCypher); 

     return cipherText; 
    } 
    catch (Exception ex) 
    { 
     throw new EncryptionException("Could not Encrypt String. See InnerException for details.", ex); 
    } 
} 

private static byte[] EncryptData(byte[] clearText, X509Certificate2 cert) 
{ 
    ContentInfo payloadInfo = new ContentInfo(clearText); 
    EnvelopedCms payloadEnvelope = new EnvelopedCms(payloadInfo); 
    CmsRecipient certHandle = new CmsRecipient(cert); 
    payloadEnvelope.Encrypt(certHandle); 
    return payloadEnvelope.Encode(); 
} 
+0

謝謝 - 這看起來正是我需要的。但是,我現在無法導入PEM文件。 PEM文件只有一個公共密鑰 - 例如:'----- BEGIN PUBLIC KEY ----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVIeL1dgxnKDy4IUmdHbIvY3Oz TlgnU + cfl9hlQgWFdFb2XwYvdj57ApeTbN8Gwaf6g/80 + 8XLOIWM + Zrl1n8ebooE TlgnU + cfl9hlQgWFdFb2XwYvdj57ApeTbN8Gwaf6g/80 + 8XLOIWM + Zrl1n8ebooE - ---結束公鑰-----(不是真正的關鍵)。我無法獲得.Import()方法來接受這一點,任何想法如何在沒有私鑰的情況下將PEM文件讀入X509Certificate2? – KenD

+0

如果文件系統上有.PEM文件,可以通過文件名導入。我認爲.cer是可以使用的擴展名(如果不是.cer,請嘗試更改擴展名)。您也可以使用Import方法使用原始字節數組。您使用不需要密碼的Import方法的重載,並且它只會使用公鑰加載X509Certificate2類。 –

+0

當我嘗試導入文件時,出現「無法找到請求的對象」錯誤。我的代碼如下所示:'X509Certificate2 cert = new x509Certificate2(); cert.Import( 「C:\\測試\\ test.cer」)'。這肯定是找到的文件,我試圖將其更改爲原始字節數組,但我仍然得到相同的錯誤:( – KenD

0

的輸出不應該匹配。出於安全原因,使用PKCS#1塊類型2的RSA公鑰加密會插入隨機填充。

0

經過一段時間的公共密鑰加密之後,我想我會貢獻給這個老的線程,以便有人在將來可能會發現它有用。

使用僅包含public.key的文件初始化X509Certificate2對象的實例不起作用。它期望一個證書,因此「無法找到請求的對象」錯誤。

以下是爲了僅使用public.key文件加密某些內容所必須完成的工作。

using System; 
using System.IO; 
using System.IO.Compression; 
using System.Security.Cryptography; 
using System.Text; 
namespace Program 
{ 
    class Program 
    { 
     static void Main(string[] args) { 

      // Load client's public key file. 
      // 
      RSAPublicKey = File.ReadAllText(@"public.key"); 

      // Decode it 
      // 
      byte[] PEMPublicKey = DecodeOpenSSLPublicKey(RSAPublicKey); 

      if(PEMPublicKey == null){ 
       throw new Exception("Could not decode RSA public key."); 
      } 

      //Create a new instance of RSACryptoServiceProvider with appropriate public key 
      // 
      RSACryptoServiceProvider RSA = DecodeX509PublicKey(PEMPublicKey); 


      // Should you want to save your XMLPublicKey for future use of RSA.FromXmlString(XMLPublicKey); 
      // 
      // String XMLPublicKey = RSA.ToXmlString(false); 


      byte[] cypher = RSA.Encrypt(Encoding.ASCII.GetBytes("This is a test"), false); 
      File.WriteAllBytes(@"cypher.txt", cypher); 

      return; 

     } 

     // The following code has been obtained from: 
     // http://csslab.s3.amazonaws.com/csslabs/Siva/opensslkey.cs 
     // All credits go to the unknown author 
     // 
     // -------- Get the binary RSA PUBLIC key -------- 
     // 
     public static byte[] DecodeOpenSSLPublicKey(String instr) { 
      const String pempubheader = "-----BEGIN PUBLIC KEY-----"; 
      const String pempubfooter = "-----END PUBLIC KEY-----"; 
      String pemstr = instr.Trim(); 
      byte[] binkey; 
      if(!pemstr.StartsWith(pempubheader) || !pemstr.EndsWith(pempubfooter)) 
       return null; 
      StringBuilder sb = new StringBuilder(pemstr); 
      sb.Replace(pempubheader, ""); //remove headers/footers, if present 
      sb.Replace(pempubfooter, ""); 

      String pubstr = sb.ToString().Trim(); //get string after removing leading/trailing whitespace 

      try { 
       binkey = Convert.FromBase64String(pubstr); 
      } 
      catch(System.FormatException) {  //if can't b64 decode, data is not valid 
       return null; 
      } 
      return binkey; 
     } 


     //------- Parses binary asn.1 X509 SubjectPublicKeyInfo; returns RSACryptoServiceProvider --- 
     public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key) { 
      // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" 
      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(x509key); 
      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 null; 

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

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

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

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

       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 null; 
       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 null; 
       int expbytes = (int)binr.ReadByte();  // should only need one byte for actual exponent data (for all useful values) 
       byte[] exponent = binr.ReadBytes(expbytes); 


       // ------- create RSACryptoServiceProvider instance and initialize with public key ----- 
       RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
       RSAParameters RSAKeyInfo = new RSAParameters(); 
       RSAKeyInfo.Modulus = modulus; 
       RSAKeyInfo.Exponent = exponent; 
       RSA.ImportParameters(RSAKeyInfo); 
       return RSA; 
      } 
      catch(Exception) { 
       return null; 
      } 

      finally { binr.Close(); } 

     } 

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


     public static byte[] Combine(byte[] first, byte[] second) { 
      byte[] ret = new byte[ first.Length + second.Length ]; 
      Buffer.BlockCopy(first, 0, ret, 0, first.Length); 
      Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); 
      return ret; 
     } 

    } 
} 

您可以使用OpenSSL的,像這樣測試的結果: openssl decrypt