使用纯 .net 框架生成和签署证书请求

Posted

技术标签:

【中文标题】使用纯 .net 框架生成和签署证书请求【英文标题】:Generate and Sign Certificate Request using pure .net Framework 【发布时间】:2018-06-20 03:36:33 【问题描述】:

我正在尝试使用纯 .net 代码创建证书请求,并根据我可用的现有 CA 证书(在 Windows 证书存储中或作为单独文件)从证书请求创建证书。

我知道我有类 X509CertificateX509Certificate2 可用于加载证书并访问它们的信息,但我在 System.Security.Cryptography 命名空间中看不到任何可用于创建的类或功能证书请求或签署此类证书请求以创建新的签名证书。

尽管documentation on the System.Security.Cryptography.Pkcs namespace 说:

System.Security.Cryptography.Pkcs 命名空间提供编程 公钥加密标准 (PKCS) 的元素,包括 签署数据、交换密钥、请求证书的方法, 公钥加解密等安全功能。

那么,我如何创建证书请求并满足该请求以仅使用来自 System.Security.Cryptography 的纯 .net 类创建新的 X509 证书?


注意:

我不想使用像 openssl 或 MakeCert 这样的外部可执行文件 我不想使用 BouncyCastle 我不想使用 Windows Certificate Enrollment API 我不想使用本机 Win32 API 函数

【问题讨论】:

看看this 和the source code 我认为如果没有你忽略的选项之一,你将无法做到这一点 @MaartenBodewes 问题不在于我是否想使用图书馆。我可以使用证书注册 API 做我想做的事。我的问题是:我是否必须使用额外的库(如证书注册库),或者该功能可能已经在默认的 .net 框架中可用,而我只是盲目地看不到它? 【参考方案1】:

简短回答:您可以从 .NET Framework 4.7.2 开始。

此功能最初以CertificateRequest 类的形式添加到.NET Core 2.0 中,该类可以构建PKCS#10 证书签名请求或X.509(自签名或链式)公钥证书。

该功能的类在 .NET Framework 4.7.2 中可用。

using (RSA parent = RSA.Create(4096))
using (RSA rsa = RSA.Create(2048))

    CertificateRequest parentReq = new CertificateRequest(
        "CN=Experimental Issuing Authority",
        parent,
        HashAlgorithmName.SHA256,
        RSASignaturePadding.Pkcs1);

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

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

    using (X509Certificate2 parentCert = parentReq.CreateSelfSigned(
        DateTimeOffset.UtcNow.AddDays(-45),
        DateTimeOffset.UtcNow.AddDays(365)))
    
        CertificateRequest req = new CertificateRequest(
            "CN=Valid-Looking Timestamp Authority",
            rsa,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pkcs1);

        req.CertificateExtensions.Add(
            new X509BasicConstraintsExtension(false, false, 0, false));

        req.CertificateExtensions.Add(
            new X509KeyUsageExtension(
                X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
                false));

        req.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection
                
                    new Oid("1.3.6.1.5.5.7.3.8")
                ,
                true));

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

        using (X509Certificate2 cert = req.Create(
            parentCert,
            DateTimeOffset.UtcNow.AddDays(-1),
            DateTimeOffset.UtcNow.AddDays(90),
            new byte[]  1, 2, 3, 4 ))
        
            // Do something with these certs, like export them to PFX,
            // or add them to an X509Store, or whatever.
        
    

如果您卡在旧版本上,答案更长:要在不添加任何新 P/Invokes 的情况下实现您的目标,您需要阅读并理解以下文档:

ITU-T X.680-201508,ASN.1 语言 IETF RFC 5280 或 ITU-T X.509,这些文档解释了 X.509 证书中的字段。 IETF RFC 2986,解释 PKCS#10 认证签名请求 ITU-T X.690,解释了 ASN.1(包括 DER)的 BER 编码系列,它告诉您如何读取和写入字节以实现 X.509 / PKCS#10 的语义。

然后您可以编写一个 DER 写入器/读取器,然后只发出您想要的字节。

【讨论】:

CertificateRequest 在 .NET Framework 4.7.2 早期访问版本 (blogs.msdn.microsoft.com/dotnet/2018/02/05/…) 中可用。当它完全发布时,我(或其他人)记得编辑答案。 嗨@bartonjs,serialNumber 应该是什么?我们如何获取/生成它? @mshwf CA/Browser Forum's Baseline Requirements (v1.7.2) 只说“CA 应生成大于零 (0) 的非顺序证书序列号,其中包含来自 CSPRNG 的至少 64 位输出。”,这是对公众的指导CA。如果你是私有 CA:纯随机,递增整数,高 4 字节为整数,低 8 字节为随机,随你喜欢(不要超过 20 字节)。 @Dbloom CreateSigningRequest 不需要序列号,因为(正如您所建议的)它没有意义。至于替换某些 CERTENROLLLib 用法,您应该提出一个新问题,说明您拥有什么,以及到目前为止您尝试过什么来替换它。 @KeithRobertson 该问题将其澄清为“......并签署该请求以创建新的 X509 证书”,这就是答案创建证书的原因。您可以使用CreateSigningRequest 而不是Create 发出签名请求【参考方案2】:

我不能对上面的答案发表评论,所以这是评论。 @基思

如果问题是用于服务器证书的私钥缺少私钥,我希望这能解释发生了什么。

要结合上述答案中的公钥和私钥,

cert = RSACertificateExtensions.CopyWithPrivateKey(cert, rsa);

这会将私钥与用于导出到 PFX 的证书捆绑在一起 File.WriteAllBytes("filename", cert.Export(X509ContentType.Pfx, "passphrase for export"));

对于上面提供的示例。 CreateSelfSigned 方法返回一个带有公钥和私钥的X509Certificate2 对象。 与根或下属签署时的位置 Create 方法只会在X509Certificate2 对象中创建公钥组件。

我认为这是因为通常的证书方法会使用 CSR 进行签名并返回公钥以供客户端接受,而客户端永远不会将私钥暴露给签名服务器。

【讨论】:

以上是关于使用纯 .net 框架生成和签署证书请求的主要内容,如果未能解决你的问题,请参考以下文章

openssl ca(签署和自建CA)

(13) openssl ca(签署和自建CA)

签署证书签名请求后,主题中缺少 UID 属性

CA证书的签发和吊销

CA证书的签发和吊销

(15) openssl签署和自签署证书的多种实现方式