创建由其他 ECDSA 证书签名的 ECDSA 证书

Posted

技术标签:

【中文标题】创建由其他 ECDSA 证书签名的 ECDSA 证书【英文标题】:Creating of ECDSA certficate signed by other ECDSA certificate 【发布时间】:2018-06-02 18:58:27 【问题描述】:

我需要创建由“根”证书(自签名,ECDSA)签名的“客户端”ECDSA 证书。 “根”证书已按照Translating Elliptic Curve parameters (BC to MS) 中的说明创建。

可以使用稍微修改的算法来创建“客户端”证书(由“根”签名)。 不同之处在于必须从“外部”提供私钥(用于从为“客户端”证书生成的密钥对中签署公钥) - 它是“根”证书的私钥。 但这就是问题所在。我找不到一种方法来获取并将私钥转换为可以传递给签名工厂的 Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters 类型。

// 1. get private-key of "Root" certificate from existing certificate:
byte[] msRootCertData = File.ReadAllBytes(@"c:\root_ecdsa_cert.pfx");
X509Certificate2 msRootCert = new X509Certificate2(msRootCertData);
ECDsaCng msRootPrivateKey = msRootCert.GetECDsaPrivateKey() as ECDsaCng;
ECParameters msRootPrivateKeyParameters = msRootPrivateKey.ExportParameters(true);

// here comes the issue:
ECPrivateKeyParameters bcRootPrivateKeysParameters = TranslateMSKeysToBouncy(msRootPrivateKeyParameters);

// 2. generate "Client" key-pair:
AsymmetricCipherKeyPair bcClientKeyPair = bcKeyGen.GenerateKeyPair();
ECPrivateKeyParameters bcClientPrivKey = (ECPrivateKeyParameters)bcClientKeyPair.Private;
ECPrivateKeyParameters bcClientPublKey = (ECPublicKeyParameters)bcClientKeyPair.Public;

// 3. create X509 certificate:
X509V3CertificateGenerator bcCertGen = new X509V3CertificateGenerator();
bcCertGen.SetPublicKey(bcClientPublKey);
// .. set subject, validity period etc
ISignatureFactory sigFac = new Asn1SignatureFactory("Sha256WithECDSA", bcRootPrivateKeysParameters);
Org.BouncyCastle.X509.X509Certificate bcClientX509Cert = bcCertGen.Generate(sigFac);
byte[] x509CertEncoded = bcClientX509Cert.GetEncoded();

// 其余同上例。

有什么提示吗? 还是有其他方法? (例如:将 X509Certificate2 的实例直接传递给 BouncyCastle 库(避免将私钥转换为 Cng),或者在没有 BouncyCastle 的情况下生成“客户端”证书) 谢谢。

【问题讨论】:

【参考方案1】:

如果您可以依赖 .NET Framework 4.7.2(或 .NET Core 2.0),则可以通过新的 CertificateRequest 类在不使用 BouncyCastle 的情况下实现:

X509Certificate2 publicPrivate;

using (ECDsa clientPrivateKey = ECDsa.Create())

    var request = new CertificateRequest(
        "CN=Et. Cetera",
        clientPrivateKey,
        HashAlgorithmName.SHA256);

    // Assuming this isn't another CA cert:
    request.CertificateExtensions.Add(
        new X509BasicConstraintsExtension(false, false, 0, false));

    // other CertificateExtensions as you desire.

    // Assign, or derive, a serial number.
    // RFC 3280 recommends that it have no more than 20 bytes encoded.
    // 12 random bytes seems long enough.
    byte[] serial = new byte[12];

    using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
    
        rng.GetBytes(serial);
    

    DateTimeOffset notBefore = DateTimeOffset.UtcNow;
    DateTimeOffset notAfter = notBefore.AddMonths(15);

    using (X509Certificate2 publicOnly = request.Create(
        msRootCert,
        notBefore,
        notAfter,
        serial))
    
        publicPrivate = publicOnly.CopyWithPrivateKey(clientPrivateKey);
    


// The original key object was disposed,
// but publicPrivate.GetECDsaPrivateKey() still works.

如果要将publicPrivate 添加到 X509Store,您需要 1) 将其导出到 PFX 并重新导入,或者 2) 更改密钥创建以使用命名密钥。否则,只会保存公共部分(在 Windows 上)。

【讨论】:

我只能使用 4.7.1.. 忘了提。对不起。还有什么办法吗? 最后一段非常有帮助 - 我正在努力让 netsh 接受我的证书,因为私钥没有正确存储。这成功了:var cert = new X509Certificate2(tempCert.Export(X509ContentType.Pfx), null as string, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet); 现在将该证书添加到 LocalMachine AuthRoot 存储后,它终于可以工作了! 终于切换到4.7.2了。 @piedar:对我有用的评论。 (无法使用 netsh 或通过第 443 页上的 IIS 管理器 (inetmgr.exe) 绑定 ecdsa 证书)。谢谢。 或者直接调用“CertificateRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.MaxValue);”

以上是关于创建由其他 ECDSA 证书签名的 ECDSA 证书的主要内容,如果未能解决你的问题,请参考以下文章

如何使用证书中的公钥检查 ECDSA(在 p-256 上)签名

在 c# 中使用 ECDSA 生成证书

如何使用椭圆曲线私钥和 ECDSA 算法签署证书?

ECDSA数字签名算法

如何使用使用 sha1ecdsa 的公钥验证数据是不是符合签名?

Pkcs11 ECDSA 签名返回 CKR_DATA_INVALID