C#生成CSR(CertificateSigningRequest)和密钥

更新于 2024-12-09

使用C#原生生成CSR(CertificateSigningRequest)和密钥。

生成的私钥妥善保存,丢失无法找回。

调用

var contents = X509Helper
    .SigningRequestHelper
    .CreateSigningRequest(
        "yourdomain.com", 
        ["*.yourdomain.com"], 
        X509Helper.AsymmetricAlgorithmType.EC_P256, 
        out string privateKey
    );

Console.WriteLine(contents);
Console.WriteLine(privateKey);

输出

-----BEGIN CERTIFICATE REQUEST-----
MIIBEjCBuQIBADAZMRcwFQYDVQQDEw55b3VyZG9tYWluLmNvbTBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABJj1dQnVVGlYxSCzucILf0j+oM1Up7q+5O+IqRXk6+91F4MY+jYmirA/l4pVnADm88Ts
4BOsZQDLL9l860HhZNGgPjA8BgkqhkiG9w0BCQ4xLzAtMCsGA1UdEQQkMCKCDnlvdXJkb21haW4u
Y29tghAqLnlvdXJkb21haW4uY29tMAoGCCqGSM49BAMCA0gAMEUCIQDGpHPxvVodEKQVFiroUQUF
36b3ODhthoghymjjFhl+TQIgAnjkgIkGlmEjr9WIJna2bEOfelRohl/rdiiKnZdlvpA=
-----END CERTIFICATE REQUEST-----

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmECK+y6xcaUBIb5mGjBFlyN2XFLH
/z5zr95ppKMK1t6hRANCAASY9XUJ1VRpWMUgs7nCC39I/qDNVKe6vuTviKkV5OvvdReDGPo2Joqw
P5eKVZwA5vPE7OATrGUAyy/ZfOtB4WTR
-----END PRIVATE KEY-----

源码

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace X509Helper
{
    public enum AsymmetricAlgorithmType
    {
        EC_P256 = 1,
        EC_P384 = 2,
        EC_P521 = 3,
        RSA_2048 = 11,
        RSA_4096 = 12,
    }
    public class SigningRequestHelper
    {
        /// <summary>
        /// 秘钥初始化参数
        /// </summary>
        private class AsymmetricAlgorithmParameters
        {
            private readonly AsymmetricAlgorithmType type;

            public AsymmetricAlgorithmParameters(AsymmetricAlgorithmType type)
            {
                this.type = type;
            }

            public bool IsECC => type < AsymmetricAlgorithmType.RSA_2048;
            public bool IsRSA => type >= AsymmetricAlgorithmType.RSA_2048;

            /// <summary>
            /// 创建非对称加密算法
            /// </summary>
            /// <returns></returns>
            public AsymmetricAlgorithm CreateAsymmetricAlgorithm()
            {
                return type switch
                {
                    AsymmetricAlgorithmType.EC_P256 => ECDsa.Create(ECCurve.NamedCurves.nistP256),
                    AsymmetricAlgorithmType.EC_P384 => ECDsa.Create(ECCurve.NamedCurves.nistP384),
                    AsymmetricAlgorithmType.EC_P521 => ECDsa.Create(ECCurve.NamedCurves.nistP521),
                    AsymmetricAlgorithmType.RSA_2048 => RSA.Create(2048),
                    AsymmetricAlgorithmType.RSA_4096 => RSA.Create(4096),
                    _ => throw new NotSupportedException()
                };
            }

            public CertificateRequest CreateCertificateRequest(X500DistinguishedName names, AsymmetricAlgorithm algorithm)
            {
                return type switch
                {
                    AsymmetricAlgorithmType.EC_P256 or AsymmetricAlgorithmType.EC_P384 or AsymmetricAlgorithmType.EC_P521
                    => new CertificateRequest(names, algorithm as ECDsa, HashAlgorithmName.SHA256),

                    AsymmetricAlgorithmType.RSA_2048 or AsymmetricAlgorithmType.RSA_4096
                    => new CertificateRequest(names, algorithm as RSA, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),

                    _ => throw new NotSupportedException()
                };
            }
        }

        /// <summary>
        /// 创建CSR
        /// </summary>
        /// <param name="commonName">公用名-commonName</param>
        /// <param name="san">备用名称-SubjectAltNames</param>
        /// <param name="alg">公钥算法,允许的值:EC-P256, EC-P384, EC-P521, RSA-2048, RSA-4096</param>
        /// <param name="privateKey">导出私钥</param>
        /// <returns></returns>
        public static string CreateSigningRequest(string commonName, List<string> san, AsymmetricAlgorithmType alg, out string privateKey)
        {
            AsymmetricAlgorithmParameters algorithm = new(alg);

            X500DistinguishedName names = new($"CN={commonName}");

            using AsymmetricAlgorithm provider = algorithm.CreateAsymmetricAlgorithm();

            CertificateRequest request = algorithm.CreateCertificateRequest(names, provider);

            //构造SAN扩展
            if (san != null && san.Count > 0)
            {
                if (!san.Contains(commonName)) san.Insert(0, commonName);
                SubjectAlternativeNameBuilder builder = new();

                foreach (string dnsName in san)
                {
                    builder.AddDnsName(dnsName);
                }

                request.CertificateExtensions.Add(builder.Build());
            }

            //导出私钥
            privateKey = GenetratePemContents(provider.ExportPkcs8PrivateKey(), "PRIVATE KEY");

            //导出CSR
            return GenetratePemContents(request.CreateSigningRequest(), "CERTIFICATE REQUEST");
        }

        private static string GenetratePemContents(byte[] data, string tag)
        {

            return $"-----BEGIN {tag}-----\r\n{Convert.ToBase64String(data, Base64FormattingOptions.InsertLineBreaks)}\r\n-----END {tag}-----\r\n";
        }

    }
}