c# - 如何使用自签名证书使用私钥创建签名的 x509certificate

标签 c# x509certificate x509certificate2

我想做的是创建一个有效的证书来签署电子文档。这需要一个带有私钥的 x509Certificate2 对象。我无法弄清楚如何使用我创建的 CA 证书创建带有私钥的签名证书。

如果可能,我只想使用 .Net 代码来执行此操作(我使用的是 4.7.2)。

我的代码创建了一个带有私钥的有效 CA 证书,但是,子证书没有私钥,因此不能用于签署电子文档。

这是我现在拥有的代码......

        private void CreateCerts()
        {
            const string issuerName = "Issuer";
            const string signerName = "Signer";

            var root = GetCertificate(issuerName, StoreName.AuthRoot);

            if (root == null)
            {
                root = CreateRoot(issuerName);
            }

            var signer = GetCertificate(signerName, StoreName.My);

            if (signer == null)
            {
                signer = CreateSigner(root, signerName);
            }
        }

        private X509Certificate2 CreateSigner(X509Certificate2 root, string name)
        {
            X509Certificate2 signer = null;
            using (RSA rsa = RSA.Create(2048))
            {

                CertificateRequest req = new CertificateRequest(
                    $"CN={name}",
                    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(
                    root,
                    DateTimeOffset.UtcNow.AddDays(-1),
                    DateTimeOffset.UtcNow.AddDays(90),
                    new byte[] { 1, 2, 3, 4, 5, 6 }))
                {

                    using (var password = new SecureString())
                    {
                        password.AppendChar('P');
                        password.AppendChar('W');

                        var childExport = cert.Export(X509ContentType.Pfx, password);

                        signer = new X509Certificate2(childExport, password);

                        AddCertToStore(signer, StoreName.My, StoreLocation.LocalMachine);
                    }
                }
            }

            return signer;
        }

        private X509Certificate2 GetCertificate(string commonName, StoreName storeName)
        {
            X509Certificate2 certificate = null;

            // Look for a certificate in the local machine store.
            // We will search for a certificate that has a CN (common name) that matches
            // the currently logged-in user.
            using (var store = new X509Store(storeName, StoreLocation.LocalMachine))
            {
                store.Open(OpenFlags.ReadOnly);
                foreach (var cert in store.Certificates)
                {
                    var subjectNames = cert.SubjectName.Name.Split(',');
                    foreach (var subjectName in subjectNames)
                    {
                        if (subjectName.Equals($"CN={commonName}"))
                        {
                            certificate = cert;
                            break;
                        }
                    }

                    if (certificate != null)
                    {
                        break;
                    }
                }
            }

            return certificate;
        }

        private X509Certificate2 CreateRoot(string name)
        {
            X509Certificate2 root = null;

            using (RSA parent = RSA.Create(4096))
            {
                CertificateRequest parentReq = new CertificateRequest(
                    $"CN={name}",
                    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)))
                {
                    using (var password = new SecureString())
                    {
                        password.AppendChar('P');
                        password.AppendChar('W');

                        var export = parentCert.Export(X509ContentType.Pfx, password);
                        root = new X509Certificate2(export, password);

                        AddCertToStore(root, StoreName.AuthRoot, StoreLocation.LocalMachine);
                    }
                }
            }

            return root;
        }

        private void AddCertToStore(X509Certificate2 cert, StoreName name, StoreLocation location)
        {
            using (var store = new X509Store(name, location))
            {
                store.Open(OpenFlags.ReadWrite);
                store.Add(cert);

                store.Close();
            }
        }

最佳答案

从另一个证书创建证书会将输入 key 视为仅是公钥(实际上,它会在构造函数中接受 PublicKey 类型)。

如果要将其与私钥配对,请使用 CopyWithPrivateKey(扩展)方法:

using (X509Certificate2 cert = req.Create(
    root,
    DateTimeOffset.UtcNow.AddDays(-1),
    DateTimeOffset.UtcNow.AddDays(90),
    new byte[] { 1, 2, 3, 4, 5, 6 }))
{
    using (X509Certificate2 certWithKey = cert.CopyWithPrivateKey(rsa))
    {
        ...
    }
}

关于c# - 如何使用自签名证书使用私钥创建签名的 x509certificate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58417805/

相关文章:

c# - 如何将 MvcHtmlString 呈现为 HTML

java - 有人可以帮助我使用 BouncyCaSTLe 实现扩展主题备用名称吗?

c# - 如何使用 System.Cryptography 解密 EncryptedAssertion

使用带有 SHA256 证书的 ECDSA 进行 C# 签名验证

c# - 如何从 X509Certificate2 获取组织名称?

c# - Lock 关键字不能按预期使用字符串

c# - 将delphi语句转换为c#

ssl - 我可以替换 client-ca-file 以使 kubernetes 上的所有用户无效吗?

wcf - 您可以使用 SoapUI 测试受证书保护的 WCF 服务吗?

c# - Azure:如何在 Azure 中的 ASP.NET Core 应用程序中写入和读取自定义日志消息?