C#从文本文件中的公钥获取CngKey对象 [英] C# Get CngKey object from public key in text file

查看:85
本文介绍了C#从文本文件中的公钥获取CngKey对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个文件,其中有多个ECDSA SHA256的公钥。该文件如下所示:

  KEY_ID:1 
状态:有效
----- BEGIN PUBLIC密钥-----
MFkwEwYHKoZIzj0CAQYIKoZIzjz0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp
WVlLfnZPoLaGKc / 2BSfYQuFIO2hfgueQINJNKJB3BJK = B

$ b状态:有效
-----公钥==
-----结束公共密钥-----

KEY_ID:3
状态:有效
-----开始公共密钥- ----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvgJ6sc2MM0AAFUJbVOD / i34YJJ8
ineqTN + DMjpI5q7fQNPEv9y2z / ecPl8qPusEND_b $ bw9bw

如何获取其中一个(或全部)键的CngKey对象(或CngKey列表)?



我尝试过类似

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

但是导入方法将System.Security.Cryptography.CryptographicException抛出为无效参数。

解决方案

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



如果您所有的密钥都在secp256r1 / NIST P-256上,那么有一种非常简单的hacky方法。



您可能已经注意到,所有键以 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE 开头。



转换

  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6Iu $ b 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 blob。



使用我们的DER-fu,我们会看到

  // SubjectPublicKeyInfo 
30 59 // SEQUENCE,0x59 == 89字节有效载荷
// AlgorithmIdentifier
30 13 // SEQUENCE,0x13 == 19字节有效载荷
// AlgorithmIdentifier.algorithm
06 07 2A 86 48 CE 3D 02 01 //对象ID 1.2.840.10045.2.1(id-ecPublicKey)
// AlgorithmIdentifier.parameters
06 08 2A 86 48 CE 3D 03 01 07 //对象ID 1.2.840.10045.3.1.7(secp256r1)
// SubjectPublicKeyInfo.publicKey
03 42 00 // BIT STRING,0x42 == 66(65)有效载荷字节,0个未使用的位
//公钥
04
92F809EAC73630CD000055096D5383FE2DF860927C8A77AA4CDF83323A48E6AE
DF40D3C4BFDCB6CFF79C3E5F2A3EEB3C7E54B888B $ C $ B $ C $ B $ C $ B b
$ b

由于算法标识符为 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



什么 CNG想要是[32位标识符] [32位小尾数长度] [已填充Qx] [已填充Qy]。 / p>

因此超级duper hacky版本是:

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

//对于ECDH而不是ECDSA,将0x53更改为0x4B。
私有静态只读字节[] s_cngBlobPrefix = {0x45、0x43、0x53、0x31、0x20、0、0、0};

私有静态CngKey ImportECDsa256PublicKey(string base64)
{
byte [] subjectPublicKeyInfo = Convert.FromBase64String(base64);

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

byte []前缀= s_secp256r1Prefix;

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

byte [] cngBlob =新字节[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);

返回CngKey.Import(cngBlob,CngKeyBlobFormat.EccPublicBlob);
}

要支持其他曲线,您需要更改CNG blob的前4个字节设置为正确的 Magic值,并将第5个字节更改为正确的长度。而且,当然,可以使用不同的SubjectPublicKeyInfo前缀,并且64不会是公钥坐标的长度(64 == 256/8 * 2)。



请参阅 C#和PHP ECDH不匹配,相反。


I have got a file in which there are several public keys for ECDSA SHA256. The file looks like:

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

How can I can get CngKey object (or list of CngKey) for one (or all) of these keys?

I have tried something like

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

but Import method throws System.Security.Cryptography.CryptographicException for invalid parameter.

解决方案

EccPublicBlob maps to BCRYPT_ECCPUBLIC_BLOB format type, not X.509 SubjectPublicKeyInfo.

If all of your keys are on secp256r1/NIST P-256 then there's a pretty straightforward hacky approach.

You may have noticed that all of your keys start with MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE. We'll see why shortly.

Convert

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaq6djyzkpHdX7kt8DsSt6IuSoXjp
WVlLfnZPoLaGKc/2BSfYQuFIO2hfgueQINJN3ZdujYXfUJ7Who+XkcJqHQ==

to bytes (or, here, hex):

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

This is a DER encoded X.509 SubjectPublicKeyInfo blob.

Using our DER-fu we see

// 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

Since the algorithm identifier is id-ecPublicKey the parameters is an OID identifying the curve (in this case, secp256r1 / NIST P-256). And "the public key" is of a format from SEC 1 v2.0 (2.3.4 Octet-String-to-Elliptic-Curve-Point Conversion).

The most common encoding is type 04, uncompressed key. (0x04 followed by Qx padded to the necessary length followed by Qy padded to the necessary length).

So, for all points encoded with type 04 on secp256r1 the byte pattern starts with

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

which happens to align to a common base64 prefix of MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE.

What CNG wants is [32-bit identifier][32-bit little-endian length][padded Qx][padded Qy].

So the super-duper hacky version is:

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

To support other curves you need to change the first 4 bytes of the CNG blob to the correct "Magic" value, and change the 5th byte to be the correct length. And, of course, different SubjectPublicKeyInfo prefixes, and 64 won't be the public key coordinate length (64 == 256 / 8 * 2). But all of that is left as an exercise to the reader.

See C# and PHP ECDH not matching for the reverse.

这篇关于C#从文本文件中的公钥获取CngKey对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆