【问题标题】:Generate and sign certificate in different machines C#在不同的机器 C# 中生成和签署证书
【发布时间】:2020-03-31 02:41:32
【问题描述】:

我需要生成证书以用于代理之间的安全通信。每个代理都会生成一个证书,并且必须将其发送到另一台机器中的系统 CA,以进行签名(并被其他代理信任)。我正在使用 C# 和以下代理代码:

 //generate certificate
        ECDsa elipticCurveNistP256Key = ECDsa.Create(ECCurve.CreateFromValue("1.2.840.10045.3.1.7")); // nistP256 curve
        CertificateRequest certificateRequest = new CertificateRequest("CN=" + agentId, elipticCurveNistP256Key, HashAlgorithmName.SHA256);
        certificateRequest.CertificateExtensions.Add(
            new X509BasicConstraintsExtension(false, false, 0, false));
        certificateRequest.CertificateExtensions.Add(
            new X509KeyUsageExtension(
                X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
                false));

        // Add the SubjectAlternativeName extension
        var sanBuilder = new SubjectAlternativeNameBuilder();
        sanBuilder.AddIpAddress(IPAddress.Parse(agentIpAddress));
        certificateRequest.CertificateExtensions.Add(sanBuilder.Build());

        certificateRequest.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection
                {
                    new Oid("1.3.6.1.5.5.7.3.8")
                },
                true));

        certificateRequest.CertificateExtensions.Add(
            new X509SubjectKeyIdentifierExtension(certificateRequest.PublicKey, false));

CA系统的代码如下:

  X509Certificate2 signedCertificate = certificateRequest.Create(
            caCertificatePFX,
            DateTimeOffset.UtcNow.AddDays(-1),
            DateTimeOffset.UtcNow.AddDays(30),
            new byte[] {1, 2, 3, 4});

当然,我也使用代码来实现机器之间的通信,这里没有展示。但我至少有两个问题:

  1. 我希望将证书生成和签名完全分开,但即使尝试了很多次,这也是我能设法开始工作的唯一代码。如果我没记错的话,这段代码在 CA 系统上创建了证书,这不是理想的场景(CA 可以访问代理私钥),但如果我没有找到更好的,我可以接受。

  2. 第二个问题是,即使我接受第一个问题,我仍然需要将 CertificateRequest 对象从一台机器发送到另一台机器,并且 CertificateRequest 不可序列化。我找到了 CreateSigningRequest() 方法,它“创建一个 ASN.1 DER 编码的 PKCS#10 CertificationRequest 值,表示当前对象的状态。”但是我还没有找到一种方法让它再次成为一个 CertificateRequest 对象,以便我可以运行 CA 系统代码。

有人知道我该怎么做吗?希望将证书生成和证书签名完全分开,但如果这不可能,至少要创建 CertificateRequest 对象。

我正在运行需要维护的 .Net Framework 4.7.2,以便使用以前开发的 Windows 窗体。

谢谢

【问题讨论】:

  • 请求/生成证书的步骤随处可见(如help.tableau.com/current/server/en-us/ssl_cert_create.htm)。所以给你的提示是,可以将 CSR 保存为文本文件然后传输。
  • 感谢您的提示。我可以保存 CreateSigningRequest() 输出,但我怎样才能重建 CertificateRequest 对象?我无法找到一种方法来做到这一点
  • 你将不得不携带和关心很多东西。这包括 PKCS#10、PKCS#7 (CMC) 格式、正确的 CSR 签名等。这是一块薄冰。

标签: c# security x509certificate2


【解决方案1】:

如您所述,没有办法回读 PKCS#10 请求。这主要是因为缺少太多的东西成为“OK”的证书颁发机构,所以拥有一个阅读器只会导致很多“糟糕的”证书颁发机构。 (由于您的 CA 不支持吊销,它也是一个“坏”的 CA,但您正在使用短生命周期证书来缓解这种情况。)

PKCS#10 请求包含:

  • 数据格式版本
  • 一个名字(大概是请求者想要的名字)
  • 公钥
  • 属性
    • 请求的扩展名在这里(EKU、主题备用名称等)
  • 签名,证明请求者拥有私钥。

如果您不使用数据格式,则数据格式版本无关紧要,并且签名对于“封闭”颁发者(仅向直接已知方颁发证书的 CA)来说并不重要。因此,您只需传输公钥和请求所需的任何其他数据(查看您当前的代码、代理 ID 和 IP 地址)。

唯一棘手的部分是发送公钥...但是使用 .NET Core 3.0+,您可以将所有密钥标准化为它们的 SubjectPublicKeyInfo 格式:

byte[] spki = elipticCurveNistP256Key.ExportSubjectPublicKeyInfo();

虽然 PublicKey 类型拥有一个 ImportSubjectPublicKeyInfo 方法会非常聪明,但这还没有发生。对于通用解析,您需要尝试所有主要的密钥类型,但由于您是另一方的封闭 CA,您可以先验地知道它是 ECDSA:

using (ECDsa clientPub = ECDsa.Create())
{
    clientPub.ImportSubjectPublicKeyInfo(transmittedSpki, out _);

    // the rest of your code goes here.
}

【讨论】:

  • 感谢您的评论。我应该提到我正在使用我必须维护的 .Net Framework 4.7.2 项目(我将编辑问题),因为它使用 Windows 窗体。 .Net Framework 有没有类似的解决方案?
  • WinForms 受 .NET Core 3.1 的支持...但您只需要想办法发送您的公钥。如果始终是 secp256r1 (NIST P-256),则 key.ExportParameters(false) 并发送 Q.X 和 Q.Y 数组。如果曲线可以变化,也发送 keyParams.Curve.Oid.Value。等等。
  • 收到签名的证书后,我需要添加之前生成的私钥。根据您在另一篇文章 (stackoverflow.com/questions/56214111/…) 中的回答,我正在使用 CopyWithPrivateKey。但是,X509Certificate 字段 HasPrivateKey 更改为 true,但 PrivateKey 保持为空。你知道为什么吗?你能帮助我吗?再次感谢 Ps。由于我的声誉,我无法在那里发表评论
  • @DavideCosta 永远不要使用 PrivateKey 属性,使用 GetRSAPrivateKey 方法(或适当的 DSA/ECDsa)。
  • 我不知道那些方法。然而,这随后被用作带有 SSL 的 RabbitMQ 客户端的证书,我真的不知道它用于获取私钥,但它不起作用,并且由于 PrivateKey 属性为空,我认为这是问题所在。我现在注意到,如果我将证书导出为字节然后将其导入回来,它已经可以工作了,我必须进一步调查。谢谢
【解决方案2】:

我强烈建议使用 CA 软件来签署证书请求。期间。

任何试图拥有您的 CA 代码的行为都会使解决方案在许多方面变得不可靠、脆弱且容易出错。有几个选项,从 Microsoft ADCS (Windows) 和 EJBCA (Windows/Linux) 开始。任何其他设计都会很糟糕。

【讨论】:

    猜你喜欢
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 2020-11-01
    • 2019-04-17
    • 2018-06-20
    • 1970-01-01
    相关资源
    最近更新 更多