代码之家  ›  专栏  ›  技术社区  ›  Veksi

如何在.NET Core中生成对CSR的响应(即编写CSR签名服务器)?

  •  0
  • Veksi  · 技术社区  · 7 年前

    我正在学习通过nuget包对证书签名请求和服务器进行签名 System.Security.Cryptography.Cng 是的。还有什么比重新创造一个更好的方法。似乎有一个块我目前没有绕过,那就是服务器签名方,即在下面的代码中 System.InvalidOperationException: 'An X509Extension with OID '2.5.29.37' has already been specified.' request.Create( 在using子句中。我看到在 http://oid-info.com/get/2.5.29.37 它是关于扩展密钥使用的。

    问题:

    1. 这个 MakeLocalhostCert 很可能是错误的,应该更改什么使其成为签署CSR的证书?
    2. 是否可以在返回的csr中添加/删除扩展/oid?我相信是的,但不知怎么的,这一部分目前我躲开了。

    我用了 https://stackoverflow.com/users/6535399/bartonjs https://stackoverflow.com/a/45240640/1332416 https://stackoverflow.com/a/44073726/1332416 才能走到这一步。:)

        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。

    <编辑2: Corefx GH中存在一个问题,跟踪这里涉及的一些问题: Security crypto - Roadmap 是的。

    1 回复  |  直到 6 年前
        1
  •  1
  •   bartonjs    7 年前

    要修复异常,您需要将代码设置为一个带有两个用途oid的eku扩展,而不是两个分别具有一个用途oid的扩展。

    // 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 package system.security.cryptography.cng了解证书签名请求和签名服务器”
      • 您的代码似乎都没有使用cng类型(这很好,您应该很少关心)。 CertificateRequest 是system.security.cryptography.x509certificates.dll的一部分,通过 Microsoft.NETCore.App
    • “是否可以在返回的CSR中添加/删除扩展/oid?”
      • 是的,在调用之前将它们添加到certificateextensions属性中 CreateSigningRequest 是的。
    • (隐含)“是否可以向 证明书 是否被退回?”
      • 是的,在调用之前将它们添加到certificateextensions属性中 Create CreateSelfSigned 是的。