与BouncyCastle一起请求的C#-ecc证书在.NET中似乎无效(C# - ecc-certificate requested with BouncyCastle seems to be invalid in .NET)

14 IT屋

As it turned out in the comments to this SO-question the source of the problem lies elsewhere so I decided to ask a new question.

I request a certificate from our PKI for a ecc keypair (curve is brainpoolP384r1). This is done via registration authority that does the proof of possession. After that I attach the private key to the issued certificate using some of the code in this helpful questions/answers: generate-certificate-using-ecdsa-in-c-sharp and translating-elliptic-curve-parameters-bc-to-ms.

After that I store the certificate with the private key in the MY-Store. So far everything works and the certificate is shown as valid in the MMC console. But if I look at it with certutil -user -store my it results in the following error (unfortunately it is in german but I will explain the errors below):

Seriennummer: 4cce6787580be9db
Aussteller: C=DE, O=TestIt, CN=ManagementCA
 Nicht vor: 01.03.2018 08:30
 Nicht nach: 29.02.2020 08:30
Antragsteller: CN=test@my.domain
Kein Stammzertifikat
Zertifikathash(sha1): 3cd94f55fa6d1c66eff9ed1cc45649006ac12616
  Schlüsselcontainer = {5C4E984A-D2DB-4BE5-BD82-7A6826C4A389}
  Eindeutiger Containername: 28083d7c2cef0143c31de128e470b486_6097f4ab-4eeb-4550-91e6-2c748bfb85d3
  Anbieter = Microsoft Software Key Storage Provider
Der private Schlüssel eignet sich nicht zum Nur-Text-Export.

Öffentlicher Schlüssel des Zertifikats:
Version: 3
Öffentlicher Schlüssel-Algorithmus:
    Algorithmus Objekt-ID: 1.2.840.10045.2.1 ECC
    Algorithmusparameter:
    0000  30 82 01 40 02 01 01 30  3c 06 07 2a 86 48 ce 3d
    0010  01 01 02 31 00 8c b9 1e  82 a3 38 6d 28 0f 5d 6f
    0020  7e 50 e6 41 df 15 2f 71  09 ed 54 56 b4 12 b1 da
    0030  19 7f b7 11 23 ac d3 a7  29 90 1d 1a 71 87 47 00
    0040  13 31 07 ec 53 30 64 04  30 7b c3 82 c6 3d 8c 15
    0050  0c 3c 72 08 0a ce 05 af  a0 c2 be a2 8e 4f b2 27
    0060  87 13 91 65 ef ba 91 f9  0f 8a a5 81 4a 50 3a d4
    0070  eb 04 a8 c7 dd 22 ce 28  26 04 30 04 a8 c7 dd 22
    0080  ce 28 26 8b 39 b5 54 16  f0 44 7c 2f b7 7d e1 07
    0090  dc d2 a6 2e 88 0e a5 3e  eb 62 d5 7c b4 39 02 95
    00a0  db c9 94 3a b7 86 96 fa  50 4c 11 04 61 04 1d 1c
    00b0  64 f0 68 cf 45 ff a2 a6  3a 81 b7 c1 3f 6b 88 47
    00c0  a3 e7 7e f1 4f e3 db 7f  ca fe 0c bd 10 e8 e8 26
    00d0  e0 34 36 d6 46 aa ef 87  b2 e2 47 d4 af 1e 8a be
    00e0  1d 75 20 f9 c2 a4 5c b1  eb 8e 95 cf d5 52 62 b7
    00f0  0b 29 fe ec 58 64 e1 9c  05 4f f9 91 29 28 0e 46
    0100  46 21 77 91 81 11 42 82  03 41 26 3c 53 15 02 31
    0110  00 8c b9 1e 82 a3 38 6d  28 0f 5d 6f 7e 50 e6 41
    0120  df 15 2f 71 09 ed 54 56  b3 1f 16 6e 6c ac 04 25
    0130  a7 cf 3a b6 af 6b 7f c3  10 3b 88 32 02 e9 04 65
    0140  65 02 01 01
Länge des öffentlichen Schlüssels: 384 Bits
Öffentlicher Schlüssel: Nicht verwendete Bits = 0
    0000  04 0e e2 21 a3 24 11 58  28 f9 12 fe 7a 2d 26 5f
    0010  ad 90 cc 79 1c b6 68 3a  b0 ff f2 df 68 17 84 cd
    0020  5f a7 9e 27 10 00 ea 6a  47 d2 74 9f c4 15 36 d1
    0030  98 5e 65 5b 2e 7e 61 d4  16 85 ed 3f 24 6b c1 2c
    0040  ef 48 b2 26 77 2b c3 61  05 44 e3 1c 2a 31 cb c1
    0050  f6 e1 cc a2 d6 3e d8 ac  36 8f ea e7 df 7d b0 9d
    0060  9d
Schlüssel-ID-Hash(rfc-sha1): e90f7f6c93e660db6742585d6dd5327f08e2469b
Schlüssel-ID-Hash(sha1): 1a0440ec89a6b951169c97d7d766c477c5a9128d
Schlüssel-ID-Hash(bcrypt-sha1): 66a3af2d30d59c36337fcf153693e6bc0111c14b
Schlüssel-ID-Hash(bcrypt-sha256): cfdf5d9b466f595f9c23dec4430a19b5134f41c882bea709ab120ed1c2496ce9

Container des öffentlichen Schlüssels:
Öffentlicher Schlüssel-Algorithmus:
    Algorithmus Objekt-ID: 1.2.840.10045.2.1 ECC
    Algorithmusparameter:
    06 09 2b 24 03 03 02 08  01 01 0b
        1.3.36.3.3.2.8.1.1.11 brainpoolP384r1
Länge des öffentlichen Schlüssels: 384 Bits
Öffentlicher Schlüssel: Nicht verwendete Bits = 0
    0000  04 0e e2 21 a3 24 11 58  28 f9 12 fe 7a 2d 26 5f
    0010  ad 90 cc 79 1c b6 68 3a  b0 ff f2 df 68 17 84 cd
    0020  5f a7 9e 27 10 00 ea 6a  47 d2 74 9f c4 15 36 d1
    0030  98 5e 65 5b 2e 7e 61 d4  16 85 ed 3f 24 6b c1 2c
    0040  ef 48 b2 26 77 2b c3 61  05 44 e3 1c 2a 31 cb c1
    0050  f6 e1 cc a2 d6 3e d8 ac  36 8f ea e7 df 7d b0 9d
    0060  9d
Schlüssel-ID-Hash(rfc-sha1): e90f7f6c93e660db6742585d6dd5327f08e2469b
Schlüssel-ID-Hash(sha1): 79d60ac0a75a30e1ba3f07ccc4dbace00610696c
Schlüssel-ID-Hash(bcrypt-sha1): 66a3af2d30d59c36337fcf153693e6bc0111c14b
Schlüssel-ID-Hash(bcrypt-sha256): cfdf5d9b466f595f9c23dec4430a19b5134f41c882bea709ab120ed1c2496ce9

FEHLER: Öffentlicher Schlüssel stimmt nicht mit gespeichertem Schlüsselsatz überein.
Das Testen der Signatur ist fehlgeschlagen.

The last part translates as:

ERROR: Certificate public key does NOT match stored keyset
Signature test FAILED 

You can see that the public key itself is identical, just the AlgorithmIdentifier is different. I do not know where the container public key comes from, the certification request matches the AlgorithmIdentifier from the certificate public key shown above.

If I try to create the csr with the AlgorithmIdentifier of the container public key shown above, I get an error in the PKI "encoded key spec not recognized".

All this leeds me to the conclusion that I am doing something wrong with the csr.

I will not post the whole code as it spawns mutliple classes just the part wher the SubjectPublicKeyInfo is constructed as this seems to be the part where things go wrong:

Code that creates valid ASN1 but results in the error above (Signature test failed)

byte[] publicKey = ecPublicKeyParameters.Q.GetEncoded();
string base64PublicKey = Convert.ToBase64String(publicKey);
// This base64PublicKey is send to the server, that handles it with the code below:

var ecPars = TeleTrusTNamedCurves.GetByName("brainpoolP384r1");
ECDomainParameters ecDomPars = new ECDomainParameters(
    ecPars.Curve,
    ecPars.G,
    ecPars.N,
    ecPars.H,
    ecPars.GetSeed());
var curve = ecDomPars.Curve;
byte[] data = Convert.FromBase64String(base64PublicKey);
var ecPoint = curve.DecodePoint(data);
ECPublicKeyParameters publicKey = new ECPublicKeyParameters(ecPoint, ecDomPars);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(ecPublicKeyParameters);

Code that creates valid ASN1 that makes the PKI complain:

// Using ECDsaCng directly in the hope that the certificate will be valid for windows:
 ECParameters ecParams = ecdsaPair.ExportParameters(false);
 ECPoint ecPoint = ecParams.Q;
 IEnumerable<byte> blobBytes = ecPoint.X.Concat(ecPoint.Y);
 byte[] eccblob = blobBytes.ToArray();
 // Sending this to the server where it will be used for SubjectPublicKeyInfo

AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(new DerObjectIdentifier("1.2.840.10045.2.1"), new DerObjectIdentifier("1.3.36.3.3.2.8.1.1.11"));
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(algorithmIdentifier, new DerBitString(eccblob));

How can I create a valid SubjectPublicKeyInfo that satisfies the PKI and the certutil validation?

解决方案

The problem with the cert-as-created seems to be that the certificate uses explicit curve domain parameters (which the RFCs frown on), and the private key did curve normalization to get back to a named curve, which you seem to have identified given the second approach.

In your "more manual" approach you didn't encode the public key correctly, you need a leading 04 to indicate that you're sending an uncompressed coordinate pair. (04 [x coordinate] [y coordinate]). The CA likely rejected your request because it didn't understand it.

04 0e e2 21 a3 24 11 58 28 f9 12 fe 7a 2d 26 5f
ad 90 cc 79 1c b6 68 3a b0 ff f2 df 68 17 84 cd
5f a7 9e 27 10 00 ea 6a 47 d2 74 9f c4 15 36 d1
98 5e 65 5b 2e 7e 61 d4 16 85 ed 3f 24 6b c1 2c
ef 48 b2 26 77 2b c3 61 05 44 e3 1c 2a 31 cb c1
f6 e1 cc a2 d6 3e d8 ac 36 8f ea e7 df 7d b0 9d
9d

Note the leading 04, and how it has odd length

FWIW: If you're on .NET Core 2.0 you can do this without BouncyCastle via System.Security.Cryptography.X509Certificates.CertificateRequest. That class is also available in the 4.7.2 early access release.

此SO问题的评论问题的根源在其他地方,所以我决定提出一个新问题。



我向PKI申请了ecc密钥对的证书(曲线为brainpoolP384r1)。
这是通过提供拥有证明的注册机构来完成的。之后,我使用以下有用的问题/解答中的一些代码将私钥附加到已颁发的证书上: generate-certificate-using-ecdsa-in-c-sharp 翻译椭圆曲线参数-bc-to-ms



之后,我将带有私钥的证书存储在我的商店。到目前为止,一切正常,并且证书在MMC控制台中显示为有效。
但是如果我用 certutil -user -store my 来查看它,则会导致以下错误(不幸的是,它是德语的,但我将在下面解释错误) :



  Seriennummer:4cce6787580be9db 
澳元:C = DE,O = TestIt,CN = ManagementCA
Nicht vor:01.03.2018 08:30
Nicht nach:29.02.2020 08:30
Antragsteller:CN=test@my.domain
Kein Stammzertifikat
Zertifikathash(sha1):3cd94f55fa6d1c66eff9ed1cc45649006ac12616
Schlüsselcontainer= {5C4E984A-D2DB-4BE5-BD82-7A6826C4A389}
Eindeutiger容器名称:28083d7c2cef0143c31de128e470b486_6097f4ab-4eeb-4550-91e6-2c748bfb85d3 = b
软件存储Schlüsseleignet可以用于Nur-Text-Export。

证书:
版本:3
ÖffentlicherSchlüssel-Algorithmus:
Algorithmus对象ID:1.2.840.10045.2.1 ECC
Algorithmus参数:
0000 30 82 01 40 02 01 01 30 3c 06 07 2a 86 48 ce 3d
0010 01 01 02 31 00 8c b9 1e 82 a3 38 6d 28 0f 5d 6f
0020 7e 50 e6 41 df 15 2f 71 09 ed 54 56 b4 12 b1 da
0030 19 7f b7 11 23 ac d3 a7 29 90 1d 1a 71 87 47 00
0040 13 31 07 ec 53 30 64 04 30 7b c3 82 c6 3d 8c 15
0050 0c 3c 72 08 0a ce 05 af a0 c2 be a2 8e 4f b2 27
0060 87 13 91 65 ef ba 91 f9 0f 8a a5 81 4a 50 3a d4
0070 eb 04 a8 c7 dd 22 ce 28 26 04 30 04 a8 c7 dd 22
0080 ce 28 26 8b 39 b5 54 16 f0 44 7c 2f b7 7d e1 07
0090 dc d2 a6 2e 88 0e a5 3e eb 62 d5 7c b4 39 02 95
00a0 db c9 94 3a b7 86 96 fa 50 4c 11 04 61 04 1d 1c
00b0 64 f0 68 cf 45 ff a2 a6 3a 81 b7 c1 3f 6b 88 47
00c0 a3 e7 7e f1 4f e3 db 7f ca fe 0c bd 1​​0 e8 e8 26
00d0 e0 34 36 d6 46 aa ef 87 b2 e2 47 d4 af 1e 8a be
00e0 1d 75 20 f9 c2 a4 5c b1 eb 8e 95 cf d5 52 62 b7
00f0 0b 29 fe ec 58 64 e1 9c 05 4f f9 91 29 28 0e 46
0100 46 21 77 91 81 11 42 82 03 41 26 3c 53 15 02 31
0110 00 8c b9 1e 82 a3 38 6d 28 0f 5d 6f 7e 50 e6 41
0120 df 15 2f 71 09 ed 54 56 b3 1f 16 6e 6c ac 04 25
0130 a7 cf 3a b6 af 6b 7f c3 10 3b 88 32 02 e9 04 65
0140 65 02 01 01
LängedesöffentlichenSchlüssels:384位
ÖffentlicherSchlüssel:Nicht verwendete位= 0
0000 04 0e e2 21 a3 24 11 58 28 f9 12 fe 7a 2d 26 5f
0010 ad 90 cc 79 1c b6 68 3a b0 ff f2 df 68 17 84 cd
0020 5f a7 9e 27 10 00 ea 6a 47 d2 74 9f c4 15 36 d1
0030 98 5e 65 5b 2e 7e 61 d4 16 85 ed 3f 24 6b c1 2c
0040 ef 48 b2 26 77 2b c3 61 05 44 e3 1c 2a 31 cb c1
0050 f6 e1 cc a2 d6 3e d8 ac 36 8f ea e7 df 7d b0 9d
006 0 9d
Schlüssel-ID-Hash(rfc-sha1):e90f7f6c93e660db6742585d6dd5327f08e2469b
Schlüssel-ID-Hash(sha1):1a0440ec89a6b951169c97d7d766c477c1cbc1cb1c1cb1c1c1b1c1c1b1c1b1e1b1e7b3b1e7b3b1e7b3b1e7b1b1e7b1e7b1b6b1e7b1b1e7b1b1b1e7b6b1b1e7b1bbbbbbbbbbbbbbbbbbbbbbf $ bSchlüssel-ID-Hash(bcrypt-sha256):cfdf5d9b466f595f9c23dec4430a19b5134f41c882bea709ab120ed1c2496ce9

Container desöffentlichenSchlüssels:$ b $bÖffentlicherSchiths2.1:
ÖffentlicherSchlüs:$ 4.1 ECC
Algorithmus参数:
06 09 2b 24 03 03 02 08 01 01 0b
1.3.36.3.3.2.8.1.1.11 brainpoolP384r1
LängedesöffentlichenSchlüssels:384位
ÖffentlicherSchlüssel:Nicht verwendete位= 0
0000 04 0e e2 21 a3 24 11 58 28 f9 12 fe 7a 2d 26 5f
0010 ad 90 cc 79 1c b6 68 3a b0 ff f2 df 68 17 84 cd
0020 5f a7 9e 27 10 00 ea 6a 47 d2 74 9f c4 15 36 d1
0030 98 5e 65 5b 2e 7e 61 d4 16 85 ed 3f 24 6b c1 2c
0040 ef 48 b2 26 77 2b c3 61 05 44 e3 1c 2a 31 cb c1
0050 f6 e1 cc a2 d6 3e d8 ac 36 8f ea e7 df 7d b0 9d
0060 9d
Schlüssel-ID-Hash(rfc-sha1) :e90f7f6c93e660db6742585d6dd5327f08e2469b
Schlüssel-ID散列(SHA1):79d60ac0a75a30e1ba3f07ccc4dbace00610696c
Schlüssel-ID散列(bcrypt-SHA1):66a3af2d30d59c36337fcf153693e6bc0111c14b
Schlüssel-ID散列(bcrypt-SHA256):cfdf5d9b466f595f9c23dec4430a19b5134f41c882bea709ab120ed1c2496ce9

FEHLER:ÖffentlicherSchlüssel刺激性行为mit pespeichertemSchlüsselsatzüberein。
Das Testen der Signatur ist fehlgeschlagen。


最后一部分翻译为:



 错误:证书公钥与存储的密钥集不匹配
签名测试失败


您可以看到公用密钥本身是相同的,只是AlgorithmIdentifier不同。我不知道容器公钥来自哪里,认证请求与上面显示的证书公钥中的AlgorithmIdentifier相匹配。



如果我尝试使用以下命令创建csr,上面显示的容器公钥的AlgorithmIdentifier,在PKI中出现"未识别编码密钥规范"错误。



所有这些使我得出结论:我在做csr出了点问题。



我不会发布整个代码,因为它会生成mutliple类,而只是构造SubjectPublicKeyInfo的那部分,因为这似乎是事情的一部分出错:



创建有效ASN1但导致上述错误的代码(签名测试失败)



  byte [] publicKey = ecPublicKeyParameters.Q.GetEncoded(); 
string base64PublicKey = Convert.ToBase64String(publicKey);
//此base64PublicKey发送到服务器,并通过以下代码处理:

var ecPars = TeleTrusTNamedCurves.GetByName(" brainpoolP384r1");
ECDomainParameters ecDomPars =新的ECDomainParameters(
ecPars.Curve,
ecPars.G,
ecPars.N,
ecPars.H,
ecPars.GetSeed( ));
var curve = ecDomPars.Curve;
byte [] data = Convert.FromBase64String(base64PublicKey);
var ecPoint = curve.DecodePoint(data);
ECPublicKeyParameters publicKey =新的ECPublicKeyParameters(ecPoint,ecDomPars);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(ecPublicKeyParameters);


创建有效PSN的有效ASN1的代码:



  //直接使用ECDsaCng希望证书对Windows有效:
ECParameters ecParams = ecdsaPair.ExportParameters(false);
ECPoint ecPoint = ecParams.Q;
IEnumerable< byte> blobBytes = ecPoint.X.Concat(ecPoint.Y);
byte [] eccblob = blobBytes.ToArray();
//将其发送到将用于SubjectPublicKeyInfo的服务器

AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(new DerObjectIdentifier(" 1.2.840.10045.2.1"),new DerObjectIdentifier(" 1.3。 36.3.3.2.8.1.1.11"));
SubjectPublicKeyInfo publicKeyInfo =新的SubjectPublicKeyInfo(algorithmIdentifier,新的DerBitString(eccblob));


如何创建满足PKI和certutil验证的有效SubjectPublicKeyInfo?


解决方案

cert-as-created的问题似乎在于证书使用了显式的曲线域参数(RFC对此不满意),并且私钥对曲线进行了归一化处理,以返回到命名曲线,您似乎已经在第二种方法中确定了该曲线。



在"更多手动"方法中,您没有正确编码公共密钥,您需要前导 04 表示您正在发送未压缩的坐标对。 ( 04 [x坐标] [y坐标] )。 CA不了解您的请求,因此拒绝了它。



  04 0e e2 21 a3 24 11 58 28 f9 12 fe 7a 2d 26 5f 
ad 90 cc 79 1c b6 68 3a b0 ff f2 df 68 17 84 cd
5f a7 9e 2710 00 ea 6a 47 d2 74 9f c4 15 36 d1
98 5e 65 5b 2e 7e 61 d4 16 85 ed 3f 24 6b c1 2c
ef 48 b2 26 77 2b c3 61 05 44 e3 1c 2a 31 cb c1
f6 e1 cc a2 d6 3e d8 ac 36 8f ea e7 df 7d b0 9d
9d


注意前导 04 ,以及它的长度如何为奇数



FWIW:如果您使用的是.NET Core 2.0,则无需通过系统来创建BouncyCastle。 Security.Cryptography.X509Certificates.CertificateRequest 。该类也可以在 4.7.2抢先体验版本。


本文地址:IT屋 » 与BouncyCastle一起请求的C#-ecc证书在.NET中似乎无效