Generate X.509 certificates with C#

Published on Wednesday, 08 November 2023 by Russ Cam

A short and sweet post for today. I've often had the need to generate X.509 certificates in code, in both PEM and PKCS#12 format. It's pretty straightforward to do so with the excellent BouncyCastle.Cryptography library.

The following generates a certificate and private key in PEM format

using System.IO;
using System.Text;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;

public static class X509CertificateGenerator
{
    public record PemCertificate(string cert, string privateKey);
    
    public static PemCertificate Generate(string subjectName)
    {
        var randomGenerator = new CryptoApiRandomGenerator();
        var random = new SecureRandom(randomGenerator);
        var serialNumber = BigIntegers.CreateRandomInRange(
            BigInteger.One, 
            BigInteger.ValueOf(long.MaxValue), random);
        var subjectDistinguishedName = new X509Name(subjectName);
        var issuerDistinguishedName = subjectDistinguishedName;
        var notBefore = DateTime.UtcNow.Date;
        var notAfter = notBefore.AddYears(1);
        var keyGenerationParameters = new KeyGenerationParameters(random, 2048);
        var keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(keyGenerationParameters);
        var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
        var issuerPrivateKey = subjectKeyPair.Private;
        
        var certificateGenerator = new X509V3CertificateGenerator();
        certificateGenerator.SetSerialNumber(serialNumber);
        certificateGenerator.AddExtension(
            X509Extensions.ExtendedKeyUsage, 
            true, 
            new ExtendedKeyUsage(KeyPurposeID.id_kp_serverAuth));
        certificateGenerator.SetIssuerDN(issuerDistinguishedName);
        certificateGenerator.SetSubjectDN(subjectDistinguishedName);
        certificateGenerator.SetNotBefore(notBefore);
        certificateGenerator.SetNotAfter(notAfter);
        certificateGenerator.SetPublicKey(subjectKeyPair.Public);
        
        var signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", issuerPrivateKey, random);
        var certificate = certificateGenerator.Generate(signatureFactory);
        var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
        
        var builder = new StringBuilder();
        using var writer = new StringWriter(builder);
        using var pemWriter = new PemWriter(writer);
        
        pemWriter.WriteObject(certificate);
        var cert = builder.ToString();
        builder.Clear();
	
        pemWriter.WriteObject(privateKeyInfo);
        var privateKey = builder.ToString();

        return new PemCertificate(cert, privateKey);
    }
}

To use

var pemCert = X509CertificateGenerator.Generate("CN=MyCertificate");

which generates output similar to the following; For the cert

-----BEGIN CERTIFICATE-----
MIICzDCCAbSgAwIBAgIIbi/O4q2a9JowDQYJKoZIhvcNAQENBQAwGDEWMBQGA1UE
AwwNTXlDZXJ0aWZpY2F0ZTAeFw0yMzExMDgwMDAwMDBaFw0yNDExMDgwMDAwMDBa
MBgxFjAUBgNVBAMMDU15Q2VydGlmaWNhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQCk1OYsZXOJIirxLigPcbBTfSLICfRvDk04wAGMWY/mcQsTE99I
memXlduf+z+/wT+hgyis27HEwtmYOUky7+Kz3QqpGwoJ/ikyIYhb2doQMEiSCs5B
4RcIkjPFqhMTmw/im2Qv0hHK4fLJjIj4DBEnQ3sJ1GXFgjKJGY1S6/yOHhi1Ki7N
riVjg1osnJLH3+WGRBehx5vNv+7B83062GfTwzOsAv6GcWTFmc1/MqdRaA5+xfqA
VcC9bKC031V1W9IgywxkZxOAbs63nx06j/PKpdZtLPidZe+YxKiYAH/WtRwnf8Fd
P/HD6nUT9p/wzQ6KxUsd4b61TEBC/6w98BDvAgMBAAGjGjAYMBYGA1UdJQEB/wQM
MAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBDQUAA4IBAQB6E55HtglZ+RAcxcYnddCB
l4IpsX1XTtrEkLSsBpX7mmzJX59tRW7cNGt2DTL1M3uRJOy4ZJazl2qwdV5hd24P
B2SVd6vT3fwoZOfBJkf5bIEgqsQjlDyp1LWiqaVRKjcp2/0tmdrWFVASiBUnyv+I
EzfDHF+QQ8TwwdQ5sauA3rfJcve+HeLPZD/WQGXI73qbvgG1rWsxccR+msLD7KGS
h6Nm8HBgS4/28lmwWxvKV4BxKp37qBgby6ss44IyEZu6GkS/yrMkpbdkDoetwFWu
UgOy/gsYNK0VCkzxQooxQAJlpAuCYxAYUcD4OlYG4jBgbIKQ8i228XpRNMGEUc47
-----END CERTIFICATE-----

and for the private key

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEApNTmLGVziSIq8S4oD3GwU30iyAn0bw5NOMABjFmP5nELExPf
SJnpl5Xbn/s/v8E/oYMorNuxxMLZmDlJMu/is90KqRsKCf4pMiGIW9naEDBIkgrO
QeEXCJIzxaoTE5sP4ptkL9IRyuHyyYyI+AwRJ0N7CdRlxYIyiRmNUuv8jh4YtSou
za4lY4NaLJySx9/lhkQXocebzb/uwfN9Othn08MzrAL+hnFkxZnNfzKnUWgOfsX6
gFXAvWygtN9VdVvSIMsMZGcTgG7Ot58dOo/zyqXWbSz4nWXvmMSomAB/1rUcJ3/B
XT/xw+p1E/af8M0OisVLHeG+tUxAQv+sPfAQ7wIDAQABAoIBAAdiD+NKMggg8yp1
Zk72yS6cKswqAfxdeCEEHK2kL1ljk9rpZ3Vxi3CCQmGil+LnbYm2/cHsX0SQc72p
RzmUXCOxUXJspyD2dPLtZJW0pttVxlJgUIfL5MO6BL6kVRFqPyZnzm+D6c8cGstj
U5lEtRn8oEy66koUAF56ugVOU7Qr24admrMylnb9iDazJav7VQfRFNvh0S+8zZyS
vsLH+yWgcxkSCrkOJN5WO8M5WfaUGqP5eZj91k7FKhrSglMZxw3tuy8FC8ivc8FX
gOo1QziBb6PbZFPToDPcWrmUVohbg/hx+wqq3L9IxJofMAjEU3dwxc71hZkiguQW
sycGtXkCgYEA1LFbggtRGHSfqjotUTCN8hY8CY0rpzWn9uD1YPWdoLZujaWXc+Zl
0e2GDxpAG0HgssXtAt31rLnyp5tRgD9/bLmH8UYapcH3a1Dd5Ny4nwHBbQkEToep
OuMoileDc8A75eggaC1++U5uZFPNMsxaV1CpJ9oLT+XX5OZAcAYMz4cCgYEAxmTF
iLdDT1Xh+AxN0rHJmYNLc6NEQvIuQ15Szd4sAJNQqTTYqDlTTyDjHc3ONWG3emmb
0/gQK1UbGe9V1RHsFH/qzDdwKFmAjOMMMmr+fnJpWRnxGLuy2UtimMkNGwbnENau
4i2J0ZhHiEd73kFp1/57b0pZdVZbMe38CsBhfVkCgYEAvdNo42CsSRJVKf1wUqTu
GuEDRlcLXdnChdFyzs2HmFcZxivDRzqbJ0hBPJtmGa2rGIMTrFpjGHyA+Fvj2n1R
gbz4ejriL4w1AT+UOt1PnQJZGrJQeLIVNKxmk0nV1Wn9S4zn13K7f1AjsEEmw66k
9obT4fB15iIxlGZ3nkOYDWMCgYALgNxTSQ0GFJy9nTkF9nkSBaLiWZ0jpJc6IMX8
Wd2DSwdqSD4nwusps2Z32+8Q+VGCL41xhPirCow/vLjlWdyuc5xRqwU90bK6eI/E
fJ+1BT1GhVKNdJSyNMTKxx2Ta5sKRuhtBObw2DuNk1eDU5enhoHxugXl39P52Sq8
FNuSQQKBgAdH8n+WMTn2C0PNViOBK1+HO0GKSC63mU0bflSB8gjWAP9fg2Y8csh9
v3uh47rWbtAk4yvnBOVj6wI42q7sBXdBPpkdRSh5QceZVT2UjgymPi0LCkWRfR7N
c05xyWIuTlcFrEQa+Bti/fX0fXiFuXcMrfr0fSfYtUtnFrvDTuUf
-----END RSA PRIVATE KEY-----

To create an X509Certificate2 from the PEM format, from .NET 5 you can use

using System.Security.Cryptography.X509Certificates;

var pkcsCert = X509Certificate2.CreateFromPem(pemCert.cert, pemCert.privateKey);

Happy generating!


Comments

comments powered by Disqus