【问题标题】:How to generate a response to a CSR in .NET Core (i.e. to write a CSR signing server)?如何在 .NET Core 中生成对 CSR 的响应(即编写 CSR 签名服务器)?
【发布时间】:2018-07-01 12:26:55
【问题描述】:

我正在通过 Nuget 包 System.Security.Cryptography.Cng 了解证书签名请求和签名服务器。还有什么比尝试重新创建一个更好的方法。似乎有一个我目前无法解决的问题,即服务器签名方,即在以下代码中,我在 using 子句中的 request.Create( 处得到 System.InvalidOperationException: 'An X509Extension with OID '2.5.29.37' has already been specified.'。我在http://oid-info.com/get/2.5.29.37 看到它是关于扩展密钥使用的。

问题:

  1. MakeLocalhostCert 可能有误,应该如何更改才能使其成为签署 CSR 的证书?
  2. 是否可以向返回的 CSR 添加/删除扩展/OID?我相信是的,但不知何故,这部分目前让我难以理解。

我在https://stackoverflow.com/a/45240640/1332416https://stackoverflow.com/a/44073726/1332416 使用了https://stackoverflow.com/users/6535399/bartonjs 的出色答案来达到这一点。 :)

    private static void CsrSigningTest()
    {
        //Both ECDSA and RSA included here, though ECDSA is probably better.
        using(ECDsa privateClientEcdsaKey = ECDsa.Create(ECCurve.NamedCurves.nistP256))
        //using(RSA privateClientRsaKey = RSA.Create(2048))
        {
            //A client creates a certificate signing request.
            CertificateRequest request = new CertificateRequest(
                new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
                privateClientEcdsaKey,
                HashAlgorithmName.SHA256);
            /*CertificateRequest request = new CertificateRequest(
                new X500DistinguishedName("CN=example.com, O=Example Ltd, OU=AllOver, L=Sacremento, ST=\"California\", C=US, E=some@example.com"),
                privateClientRsaKey,
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pkcs1);*/

            var sanBuilder = new SubjectAlternativeNameBuilder();
            sanBuilder.AddDnsName("example.com");
            request.CertificateExtensions.Add(sanBuilder.Build());

            //Not a CA, a server certificate.
            request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
            request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
            request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));

            byte[] derEncodedCsr = request.CreateSigningRequest();
            var csrSb = new StringBuilder();
            csrSb.AppendLine("-----BEGIN CERTIFICATE REQUEST-----");
            csrSb.AppendLine(Convert.ToBase64String(derEncodedCsr));
            csrSb.AppendLine("-----END CERTIFICATE REQUEST-----");

            //Thus far OK, this csr seems to be working when using an online checker.
            var csr = csrSb.ToString();

            //Now, sending this to a server... How does the server function:
            //1) Read the CSR to be processed?
            //2) How does this CSR get signed?
            //In the following, can the signing cert be self-signed could be had from
            //https://stackoverflow.com/a/45240640/1332416

            byte[] serial = new byte[16];
            using(var rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(serial);
            }

            DateTimeOffset notBefore = DateTimeOffset.UtcNow;
            DateTimeOffset notAfter = notBefore.AddYears(1);
            var issuerCertificate = MakeLocalhostCert();
            //For the part 1) there, this doesn't seem to work, likely since CSR isn't a X509 certificate.
            //But then again, there doesn't seem to be anything in CertificateRequest to read this.
            //In reality in the server the prologue and epilogue strings should be removed and the string read.
            //var testRequest = new X509Certificate2(derEncodedCsr);
            using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
            {
                //How to add extensions here?
                var csrResSb = new StringBuilder();
                csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
                csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.GetRawCertData()));
                csrResSb.AppendLine("-----END CERTIFICATE-----");

                var signedCert = csrResSb.ToString();
            }
        }
    }

    private static X509Certificate2 MakeLocalhostCert()
    {
        using(ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP384))
        {
            var request = new CertificateRequest(
                "CN=localhost",
                key,
                HashAlgorithmName.SHA384);

            request.CertificateExtensions.Add(
                new X509BasicConstraintsExtension(true, false, 0, true));

            const X509KeyUsageFlags endEntityTypicalUsages =
                X509KeyUsageFlags.DataEncipherment |
                X509KeyUsageFlags.KeyEncipherment |
                X509KeyUsageFlags.DigitalSignature |
                X509KeyUsageFlags.NonRepudiation |
                X509KeyUsageFlags.KeyCertSign;

            request.CertificateExtensions.Add(
                new X509KeyUsageExtension(endEntityTypicalUsages, true));

            var sanBuilder = new SubjectAlternativeNameBuilder();
            sanBuilder.AddDnsName("localhost");
            sanBuilder.AddIpAddress(IPAddress.Loopback);
            sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);

            request.CertificateExtensions.Add(sanBuilder.Build());

            /*request.CertificateExtensions.Add(
                new X509EnhancedKeyUsageExtension(
                    new OidCollection
                    {
                // server and client authentication
                new Oid("1.3.6.1.5.5.7.3.1"),
                new Oid("1.3.6.1.5.5.7.3.2")
                    },
                    false));*/

            DateTimeOffset now = DateTimeOffset.UtcNow.AddMinutes(-1);

            return request.CreateSelfSigned(now, now.AddYears(2));
        }
    }

一旦我应用了关于 OID 的修复并将最后一位更改为

using(X509Certificate2 responseToCsr = request.Create(issuerCertificate, notBefore, notAfter, serial))
            {
                request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(issuerCertificate.PublicKey, false));
                var csrResSb = new StringBuilder();
                csrResSb.AppendLine("-----BEGIN CERTIFICATE-----");
                csrResSb.AppendLine(Convert.ToBase64String(responseToCsr.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
                csrResSb.AppendLine("-----END CERTIFICATE-----");

                var signedCert = csrResSb.ToString();
            }

我在signedCert 中取回了一个证书,该证书看起来像是已签署的 CSR。如果从实际的 CSR 文件中读取,缺少的部分将是构建 CSR。

CoreFx GH 中存在一个问题,跟踪此处涉及的一些问题:Security crypto - Roadmap

很高兴知道:How to load a certificate request and create a certificate from it in .NETHow to convert a CSR text file into .NET Core/ Standard CertificateRequest for Signing?How to convert a CSR text file into .NET Core/ Standard CertificateRequest for Signing? 了解更多关于 .NET5/6 的信息。

【问题讨论】:

  • 我会选择标准的 CA 软件来做这件事。尝试使用原始 API(.NET、OpenSSL 等)来模仿 CA 服务器功能从来都不是一个好主意。
  • 出于好奇,您是否有一些标准的 CA 软件?我主要是在这里自学,熬夜来自学这方面的知识,并认为如果有一些事情对某人“揭示”,我可能会花点精力,更快,甚至可能对他人有所帮助。例如,临时签名证书中的一些位(启用 CA 等)。 :)
  • 记住两个:Microsoft ADCS(独立的会很好)和 EJBCA。
  • 嘿!谢谢!这让我有点感动。同时我对内容进行了一些编辑,我取得了一些进展。 :)

标签: .net .net-core x509certificate2


【解决方案1】:

要修复您的异常,您希望让您的代码设置一个具有两个目的 OID 的 EKU 扩展,而不是两个各一个的扩展。

// Defined two EKU extensions
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.8") }, false));

// One extension carrying multiple purpose OIDs
request.CertificateExtensions.Add(
    new X509EnhancedKeyUsageExtension(
        new OidCollection
        {
            new Oid("1.3.6.1.5.5.7.3.1"),
            new Oid("1.3.6.1.5.5.7.3.8"),
        },
        false));

那么您的标题/问题中还有一些其他问题:

  • “如何在 .NET Core 中生成对 CSR 的响应(即编写 CSR 签名服务器)?”
    • 此代码不读取 CSR,因此它没有响应 CSR。
      • 该课程的目的是满足单元测试和其他开发环境需求,并能够生成 CSR 以发送给真正的 CA 产品。
      • .NET Core 甚至不具备读取 CSR 的能力,只需编写它们即可。
  • “我正在通过 Nuget 包 System.Security.Cryptography.Cng 了解证书签名请求和签名服务器”
    • 您的代码似乎都没有使用 Cng 类型(这很好,您应该很少关心)。 CertificateRequest 是 System.Security.Cryptography.X509Certificates.dll 的一部分,通过 Microsoft.NETCore.App 公开
  • “是否可以向返回的 CSR 添加/删除扩展/OID?”
    • 是的,您在调用 CreateSigningRequest 之前将它们添加到 CertificateExtensions 属性中。
  • (暗示)“是否可以在返回的证书中添加/删除扩展/OID?”
    • 是的,您在调用CreateCreateSelfSigned 之前将它们添加到CertificateExtensions 属性中。

【讨论】:

  • 是的,代码不会从文件中读取 CSR,因为您注意到没有设施(即使它们在那里,我也可能错过了 :))。这就是为什么我在没有阅读 CSR 文本的情况下在实际课程中胡闹。但是感谢您的 OID 修复,如果我不将 CSR 作为文本阅读,我设法将看起来像是签署 CSR 的东西解析在一起。也许也有一些方法可以轻松解析 DER 编码的文本文件......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
  • 1970-01-01
  • 1970-01-01
  • 2013-07-21
  • 1970-01-01
  • 2012-01-20
  • 2020-11-19
相关资源
最近更新 更多