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

Generate and Sign Certificate Request using pure .net Framework

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

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

虽然 documentation on the System.Security.Cryptography.Pkcs namespace 说:

The System.Security.Cryptography.Pkcs namespace provides programming elements for Public Key Cryptography Standards (PKCS), including methods for signing data, exchanging keys, requesting certificates, public key encryption and decryption, and other security functions.

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


注:

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

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

该功能的 classes 在 .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 or ITU-T X.509,解释 X.509 证书中字段的文档。
  • IETF RFC 2986,解释 PKCS#10 认证签名请求
  • ITU-T X.690,解释了 ASN.1(包括 DER)的 BER 编码系列,告诉您如何读写字节以实现 X.509 / PKCS#10 的语义。

然后你可以写一个 DER writer/reader,然后只发出你想要的字节。

上面的回答我无法评论,所以这里作为评论。 @基思

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

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

cert = RSACertificateExtensions.CopyWithPrivateKey(cert, rsa);

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

对于上面提供的示例。 方法 CreateSelfSigned return 是一个带有 public 和私钥的 X509Certificate2 对象。 Where as when signing against a root, or undergood Create 方法只会在 X509Certificate2 对象中创建 public 关键组件。

我认为这是因为通常的证书方法会使用 CSR 进行签名,return 客户端接受的 public 密钥永远不会将私钥暴露给签名服务器.