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!