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。
什麼是全面的答案。拍手我的手。 – petriq