2017-06-12 28 views
0

我有一個文件,其中有幾個ECDSA SHA256的公鑰。該文件看起來像:C#從文本文件中的公鑰獲取CngKey對象

KEY_ID: 1 
STATUS: VALID 
-----BEGIN PUBLIC KEY----- 
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp 
WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ== 
-----END PUBLIC KEY----- 

KEY_ID: 2 
STATUS: VALID 
-----BEGIN PUBLIC KEY----- 
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+Y5mYZL/EEY9zGji+hrgGkeoyccK 
D0/oBoSDALHc9+LXHKsxXiEV7/h6d6+fKRDb6Wtx5cMzXT9HyY+TjPeuTg== 
-----END PUBLIC KEY----- 

KEY_ID: 3 
STATUS: VALID 
-----BEGIN PUBLIC KEY----- 
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvgJ6sc2MM0AAFUJbVOD/i34YJJ8 
ineqTN+DMjpI5q7fQNPEv9y2z/ecPl8qPus8flS4iLOOxdwGoF1mU9lwfA== 
-----END PUBLIC KEY----- 

我如何能得到CngKey對象(或列表CngKey的)這些鍵(或全部)?

我已經試過類似

string plainTextKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjpWVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ=="; 
byte[] publicKeyBytes = Convert.FromBase64String(plainTextKey); 
CngKey ret = CngKey.Import(publicKeyBytes, CngKeyBlobFormat.EccPublicBlob); 

但導入方法拋出System.Security.Cryptography.CryptographicException無效參數。

回答

2

EccPublicBlob映射到BCRYPT_ECCPUBLIC_BLOB格式類型,而不是X.509 SubjectPublicKeyInfo進行。

如果你的所有密鑰都在secp256r1/NIST P-256上,那麼這裏有一個非常簡單的hacky方法。

您可能已經注意到您的所有密鑰都以MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE開頭。我們馬上就會明白爲什麼。

轉換

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp 
WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ== 

到字節(或者,在這裏,十六進制):

30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A 
86 48 CE 3D 03 01 07 03 42 00 04 6A AE 9D 8F 2C 
E4 A4 77 57 EE 4B 7C 0E C4 AD E8 8B 92 A1 78 E9 
59 59 4B 7E 76 4F A0 B6 86 29 CF F6 05 27 D8 42 
E1 48 3B 68 5F 82 E7 90 20 D2 4D DD 97 6E 8D 85 
DF 50 9E D6 86 8F 97 91 C2 6A 1D 

這是DER編碼X.509 SubjectPublicKeyInfo團塊。

使用我們的DER-FU我們看到

// SubjectPublicKeyInfo 
30 59 // SEQUENCE, 0x59 == 89 bytes of payload 
    // AlgorithmIdentifier 
    30 13 // SEQUENCE, 0x13 == 19 bytes of payload 
     // AlgorithmIdentifier.algorithm 
     06 07 2A 86 48 CE 3D 02 01 // OBJECT ID 1.2.840.10045.2.1 (id-ecPublicKey) 
     // AlgorithmIdentifier.parameters 
     06 08 2A 86 48 CE 3D 03 01 07 // OBJECT ID 1.2.840.10045.3.1.7 (secp256r1) 
    // SubjectPublicKeyInfo.publicKey 
    03 42 00 // BIT STRING, 0x42 == 66 (65) payload bytes, 0 unused bits 
     // "the public key" 
     04 
     92F809EAC73630CD000055096D5383FE2DF860927C8A77AA4CDF83323A48E6AE 
     DF40D3C4BFDCB6CFF79C3E5F2A3EEB3C7E54B888B38EC5DC06A05D6653D9707C 

由於算法標識符是id-ecPublicKey的參數是一個OID識別的曲線(在這種情況下,secp256r1/NIST P-256)。而「公鑰」格式爲SEC 1 v2.0(2.3.4八位字符串到橢圓曲線點轉換)。

最常見的編碼是類型04,未壓縮的密鑰。 (0x04,然後將Qx填充到必要的長度,然後將Qy填充到必要的長度)。

所以,在secp256r1字節模式與04型編碼的所有點,始於

30 59 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A 
86 48 CE 3D 03 01 07 03 42 00 04 

這恰好對準的MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE一個共同的base64前綴。

什麼CNG wants是[32位標識符] [32位小尾數長度] [填充Qx] [填充Qy]。

所以超級騙子哈克的版本是:

private static readonly byte[] s_secp256r1Prefix = 
    Convert.FromBase64String("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE"); 

// For ECDH instead of ECDSA, change 0x53 to 0x4B. 
private static readonly byte[] s_cngBlobPrefix = { 0x45, 0x43, 0x53, 0x31, 0x20, 0, 0, 0 }; 

private static CngKey ImportECDsa256PublicKey(string base64) 
{ 
    byte[] subjectPublicKeyInfo = Convert.FromBase64String(base64); 

    if (subjectPublicKeyInfo.Length != 91) 
     throw new InvalidOperationException(); 

    byte[] prefix = s_secp256r1Prefix; 

    if (!subjectPublicKeyInfo.Take(prefix.Length).SequenceEqual(prefix)) 
     throw new InvalidOperationException(); 

    byte[] cngBlob = new byte[s_cngBlobPrefix.Length + 64]; 
    Buffer.BlockCopy(s_cngBlobPrefix, 0, cngBlob, 0, s_cngBlobPrefix.Length); 

    Buffer.BlockCopy(
     subjectPublicKeyInfo, 
     s_secp256r1Prefix.Length, 
     cngBlob, 
     s_cngBlobPrefix.Length, 
     64); 

    return CngKey.Import(cngBlob, CngKeyBlobFormat.EccPublicBlob); 
} 

要支持其他的曲線則需要前4個字節的CNG BLOB更改爲正確的「魔術」值,第五個字節改爲是正確的長度。當然,不同的SubjectPublicKeyInfo前綴和64將不會是公鑰座標長度(64 == 256/8 * 2)。但是,所有這些都是給讀者的一個練習。

請參閱C# and PHP ECDH not matching

+0

什麼是全面的答案。拍手我的手。 – petriq

0

我一直在解析這樣的文件40年。這樣的代碼有很長的歷史

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 


namespace ConsoleApplication62 
{ 
    enum State 
    { 
     FIND_KEY, 
     GET_STATUS, 
     GET_KEY_STRINGS 
    } 
    class Program 
    { 
     const string FILENAME = @"c:\temp\test.txt"; 
     static void Main(string[] args) 
     { 
      new Key(FILENAME); 

     } 

    } 
    public class Key 
    { 
     public static List<Key> keys = new List<Key>(); 

     public int id { get; set; } 
     public Boolean status { get; set; } 
     List<string> keysStrs = new List<string>(); 

     public Key() { } 
     public Key(string filename) 
     { 
      StreamReader reader = new StreamReader(filename); 
      string inputLine = ""; 

      State state = State.FIND_KEY; 
      Key newKey = null; 
      while((inputLine = reader.ReadLine()) != null) 
      { 
       inputLine = inputLine.Trim(); 
       if(inputLine.Length > 0) 
       { 
        switch (state) 
        { 
         case State.FIND_KEY : 
          if(inputLine.StartsWith("KEY_ID:")) 
          { 
           newKey = new Key(); 
           keys.Add(newKey); 
           int id = int.Parse(inputLine.Substring(inputLine.LastIndexOf(" "))); 
           newKey.id = id; 
           state = State.GET_STATUS; 
          } 
          break; 

         case State.GET_STATUS: 
          if (inputLine.StartsWith("STATUS:")) 
          { 
           string status = inputLine.Substring(inputLine.LastIndexOf(" ")).Trim(); 
           newKey.status = status == "VALID" ? true : false; 
           state = State.GET_KEY_STRINGS; 
          }        
          break; 

         case State.GET_KEY_STRINGS: 
          if (!inputLine.StartsWith("-")) 
          { 
           newKey.keysStrs.Add(inputLine.Trim()); 
          } 
          else 
          { 
           if (inputLine.Contains("END PUBLIC KEY")) 
           { 
            state = State.FIND_KEY; 
           } 
          } 
          break; 
        } 
       } 

      } 
     } 
    } 
} 
+0

我知道如何用鍵分析文件,但是我無法從解析的明文公鑰獲取CngKey對象。 – petriq

+0

你確定你正在使用包括SHA256在內的正確設置嗎?在解密/加密之前,我看到過類似的錯誤。上次我有問題有5個不同的參數設置,所以我寫了一個5嵌套for循環,並嘗試所有選項,直到我找到正確的選項。在循環中放置一些異常處理程序,以便在出現異常時繼續執行代碼。我發現了屬性的正確組合。文件有時非常模糊。 – jdweng