Generate a new X509Certificate2 with extensions and private key, in C#
There is very little documentation and examples around on how to do this. And when you get stuck on something, you might not know what to search for. So here is a complete example for C#.
I’m using a NuGet from Bouncy Castle (bouncycastle.org).
Contents
- The example code
- Relative Distinguished Names (links to specifications)
- Bouncy Castle Signature algorithm strings
- Bouncy Castle X509Extensions
- KeyUsage values
The code
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Pkcs; 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.Pkcs; using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; using System; using System.Security.Cryptography.X509Certificates; using X509Certificate = Org.BouncyCastle.X509.X509Certificate; namespace SignedXmlValidation.CertStuff { public class CertCreator { private const string SignatureAlgorithm = "SHA256WithRSA"; public static X509Certificate2 GenerateX509Certificate(string commonName) { var keyPair = GetAsymmetricCipherKeyPair(); var certificateGenerator = GetX509V3CertificateGenerator(keyPair); // Note: There are more parameters than just commonName certificateGenerator.SetSubjectAndIssuer(commonName); var bouncyCastleCertificate = GenerateBouncyCastleCertificate( keyPair, certificateGenerator); return GenerateX509CertificateWithPrivateKey( keyPair, bouncyCastleCertificate); } private static AsymmetricCipherKeyPair GetAsymmetricCipherKeyPair() { var keyPairGen = new RsaKeyPairGenerator(); var keyParams = new KeyGenerationParameters( new SecureRandom(new CryptoApiRandomGenerator()), 2048); keyPairGen.Init(keyParams); var keyPair = keyPairGen.GenerateKeyPair(); return keyPair; } private static X509V3CertificateGenerator GetX509V3CertificateGenerator( AsymmetricCipherKeyPair keyPair) { var gen = new X509V3CertificateGenerator(); gen.SetSerialNumber(BigInteger.ProbablePrime(120, new Random())); gen.SetNotAfter(DateTime.Now.AddDays(1)); gen.SetNotBefore(DateTime.Now.AddDays(-1)); gen.SetPublicKey(keyPair.Public); gen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); var ski = new SubjectKeyIdentifier( SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public)); gen.AddExtension(X509Extensions.SubjectKeyIdentifier, false, ski); var keyUsage = new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyCertSign); gen.AddExtension(X509Extensions.KeyUsage, true, keyUsage); return gen; } private static X509Certificate GenerateBouncyCastleCertificate( AsymmetricCipherKeyPair keyPair, X509V3CertificateGenerator gen) { ISignatureFactory sigFact = new Asn1SignatureFactory( SignatureAlgorithm, keyPair.Private); var bcCert = gen.Generate(sigFact); return bcCert; } private static X509Certificate2 GenerateX509CertificateWithPrivateKey( AsymmetricCipherKeyPair keyPair, X509Certificate bcCert) { var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private); var asn1Seq = (Asn1Sequence)Asn1Object.FromByteArray( privateKeyInfo.ParsePrivateKey().GetDerEncoded()); var rsaPrivateKeyStruct = RsaPrivateKeyStructure.GetInstance(asn1Seq); var rsa = DotNetUtilities.ToRSA(rsaPrivateKeyStruct); var x509Cert = new X509Certificate2(bcCert.GetEncoded()); return x509Cert.CopyWithPrivateKey(rsa); } } }
I used an extension method for setting the Subject and Issuer:
using System.Collections; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.X509; namespace SignedXmlValidation.CertStuff { public static class CertExtensions { public static void SetSubjectAndIssuer(this X509V3CertificateGenerator gen, string commonName = null, string country = null, string organizationalUnit = null, string locality = null ) { IDictionary attrs = new Hashtable(); if (commonName != null) attrs[X509Name.CN] = commonName; if (country != null) attrs[X509Name.C] = country; if (organizationalUnit != null) attrs[X509Name.O] = organizationalUnit; if (locality != null) attrs[X509Name.L] = locality; IList ord = new ArrayList(); if (commonName != null) ord.Add(X509Name.CN); if (country != null) ord.Add(X509Name.C); if (organizationalUnit != null) ord.Add(X509Name.O); if (locality != null) ord.Add(X509Name.L); gen.SetSubjectDN(new X509Name(ord, attrs)); gen.SetIssuerDN(new X509Name(ord, attrs)); } } }
Relative Distinguished Names (RDN)
Below is a list of some values you can use when you specify distinguished names.
You find the specification for X.509 PKI certificates here: https://tools.ietf.org/html/rfc5280
The spec for OID‘s are in appendix A: https://tools.ietf.org/html/rfc5280#appendix-A
You can find Short names in this spec: https://tools.ietf.org/html/rfc4519
Short Name | OID | Description |
---|---|---|
C | 2.5.4.6 | Country |
O | 2.5.4.10 | Organization |
OU | 2.5.4.11 | Organizational Unit |
T | 2.5.4.12 | Title |
Street | 2.5.4.9 | Street |
DnQualifier | 2.5.4.46 | Distinguished Name Qualifier |
ST | 2.5.4.8 | State or Province name |
CN | 2.5.4.3 | Common name (e.g., “Susan Housley”) |
SERIALNUMBER | 2.5.4.5 | Serial number |
L | 2.5.4.7 | Locality |
Name | 2.5.4.41 | Name |
Surname | 2.5.4.4 | Surname |
GivenName | 2.5.4.42 | Given name |
Initials | 2.5.4.43 | Initials |
Pseudonym | 2.5.4.65 | Pseudonym |
BusinessCategory | 2.5.4.15 | Business Category |
Signature Algorithm Strings
There were found in Bouncy Castle code (sorted):
- “DSAWITHSHA1”
- “ECDSAWITHSHA1”
- “GOST3411-2012-256WITHECGOST3410-2012-256”
- “GOST3411-2012-256WITHGOST3410-2012-256”
- “GOST3411-2012-512WITHECGOST3410-2012-512”
- “GOST3411-2012-512WITHGOST3410-2012-512”
- “GOST3411WITHECGOST3410”
- “GOST3411WITHECGOST3410-2001”
- “GOST3411WITHECGOST3410-2012-256”
- “GOST3411WITHECGOST3410-2012-512”
- “GOST3411WITHGOST3410”
- “GOST3411WITHGOST3410-2001”
- “GOST3411WITHGOST3410-2012-256”
- “GOST3411WITHGOST3410-2012-512”
- “GOST3411WITHGOST3410-94”
- “MD2WITHRSA”
- “MD2WITHRSAENCRYPTION”
- “MD5WITHRSA”
- “MD5WITHRSAENCRYPTION”
- “RIPEMD128WITHRSA”
- “RIPEMD128WITHRSAENCRYPTION”
- “RIPEMD160WITHPLAIN-ECDSA”
- “RIPEMD160WITHRSA”
- “RIPEMD160WITHRSAENCRYPTION”
- “RIPEMD256WITHRSA”
- “RIPEMD256WITHRSAENCRYPTION”
- “SHA-1WITHDSA”
- “SHA-1WITHRSA”
- “SHA-1WITHRSAENCRYPTION”
- “SHA-224WITHRSA”
- “SHA-224WITHRSAENCRYPTION”
- “SHA-256WITHRSA”
- “SHA-256WITHRSAENCRYPTION”
- “SHA-384WITHRSA”
- “SHA-384WITHRSAENCRYPTION”
- “SHA-512(224)WITHRSA”
- “SHA-512(224)WITHRSAENCRYPTION”
- “SHA-512(256)WITHRSA”
- “SHA-512(256)WITHRSAENCRYPTION”
- “SHA-512WITHRSA”
- “SHA-512WITHRSAENCRYPTION”
- “SHA1WITHCVC-ECDSA”
- “SHA1WITHDSA”
- “SHA1WITHECDSA”
- “SHA1WITHPLAIN-ECDSA”
- “SHA1WITHRSA”
- “SHA1WITHRSAANDMGF1”
- “SHA1WITHRSAENCRYPTION”
- “SHA224WITHCVC-ECDSA”
- “SHA224WITHDSA”
- “SHA224WITHECDSA”
- “SHA224WITHPLAIN-ECDSA”
- “SHA224WITHRSA”
- “SHA224WITHRSAANDMGF1”
- “SHA224WITHRSAENCRYPTION”
- “SHA256WITHCVC-ECDSA”
- “SHA256WITHDSA”
- “SHA256WITHECDSA”
- “SHA256WITHPLAIN-ECDSA”
- “SHA256WITHRSA”
- “SHA256WITHRSAANDMGF1”
- “SHA256WITHRSAENCRYPTION”
- “SHA256WITHSM2”
- “SHA256WITHXMSS”
- “SHA256WITHXMSSMT”
- “SHA3-224WITHDSA”
- “SHA3-224WITHECDSA”
- “SHA3-224WITHRSA”
- “SHA3-224WITHRSAANDMGF1”
- “SHA3-224WITHRSAENCRYPTION”
- “SHA3-256WITHDSA”
- “SHA3-256WITHECDSA”
- “SHA3-256WITHRSA”
- “SHA3-256WITHRSAANDMGF1”
- “SHA3-256WITHRSAENCRYPTION”
- “SHA3-384WITHDSA”
- “SHA3-384WITHECDSA”
- “SHA3-384WITHRSA”
- “SHA3-384WITHRSAANDMGF1”
- “SHA3-384WITHRSAENCRYPTION”
- “SHA3-512WITHDSA”
- “SHA3-512WITHECDSA”
- “SHA3-512WITHRSA”
- “SHA3-512WITHRSAANDMGF1”
- “SHA3-512WITHRSAENCRYPTION”
- “SHA3-512WITHSPHINCS256”
- “SHA384WITHCVC-ECDSA”
- “SHA384WITHDSA”
- “SHA384WITHECDSA”
- “SHA384WITHPLAIN-ECDSA”
- “SHA384WITHRSA”
- “SHA384WITHRSAANDMGF1”
- “SHA384WITHRSAENCRYPTION”
- “SHA512(224)WITHRSA”
- “SHA512(224)WITHRSAENCRYPTION”
- “SHA512(256)WITHRSA”
- “SHA512(256)WITHRSAENCRYPTION”
- “SHA512WITHCVC-ECDSA”
- “SHA512WITHDSA”
- “SHA512WITHECDSA”
- “SHA512WITHPLAIN-ECDSA”
- “SHA512WITHRSA”
- “SHA512WITHRSAANDMGF1”
- “SHA512WITHRSAENCRYPTION”
- “SHA512WITHSPHINCS256”
- “SHA512WITHXMSS”
- “SHA512WITHXMSSMT”
- “SHAKE128WITHXMSS”
- “SHAKE128WITHXMSSMT”
- “SHAKE256WITHXMSS”
- “SHAKE256WITHXMSSMT”
- “SM3WITHSM2”
X509Extensions (sorted)
Code from Bouncy Castle, but usage is documented in section 4.2 in the spec: https://tools.ietf.org/html/rfc5280#section-4.2
- DerObjectIdentifier AuditIdentity = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.4”);
- DerObjectIdentifier AuthorityInfoAccess = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.1”);
- DerObjectIdentifier AuthorityKeyIdentifier = new DerObjectIdentifier(“2.5.29.35”);
- DerObjectIdentifier BasicConstraints = new DerObjectIdentifier(“2.5.29.19”);
- DerObjectIdentifier BiometricInfo = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.2”);
- DerObjectIdentifier CertificateIssuer = new DerObjectIdentifier(“2.5.29.29”);
- DerObjectIdentifier CertificatePolicies = new DerObjectIdentifier(“2.5.29.32”);
- DerObjectIdentifier CrlDistributionPoints = new DerObjectIdentifier(“2.5.29.31”);
- DerObjectIdentifier CrlNumber = new DerObjectIdentifier(“2.5.29.20”);
- DerObjectIdentifier DeltaCrlIndicator = new DerObjectIdentifier(“2.5.29.27”);
- DerObjectIdentifier ExpiredCertsOnCrl = new DerObjectIdentifier(“2.5.29.60”);
- DerObjectIdentifier ExtendedKeyUsage = new DerObjectIdentifier(“2.5.29.37”);
- DerObjectIdentifier FreshestCrl = new DerObjectIdentifier(“2.5.29.46”);
- DerObjectIdentifier InhibitAnyPolicy = new DerObjectIdentifier(“2.5.29.54”);
- DerObjectIdentifier InstructionCode = new DerObjectIdentifier(“2.5.29.23”);
- DerObjectIdentifier InvalidityDate = new DerObjectIdentifier(“2.5.29.24”);
- DerObjectIdentifier IssuerAlternativeName = new DerObjectIdentifier(“2.5.29.18”);
- DerObjectIdentifier IssuingDistributionPoint = new DerObjectIdentifier(“2.5.29.28”);
- DerObjectIdentifier KeyUsage = new DerObjectIdentifier(“2.5.29.15”);
- DerObjectIdentifier LogoType = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.12”);
- DerObjectIdentifier NameConstraints = new DerObjectIdentifier(“2.5.29.30”);
- DerObjectIdentifier NoRevAvail = new DerObjectIdentifier(“2.5.29.56”);
- DerObjectIdentifier PolicyConstraints = new DerObjectIdentifier(“2.5.29.36”);
- DerObjectIdentifier PolicyMappings = new DerObjectIdentifier(“2.5.29.33”);
- DerObjectIdentifier PrivateKeyUsagePeriod = new DerObjectIdentifier(“2.5.29.16”);
- DerObjectIdentifier QCStatements = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.3”);
- DerObjectIdentifier ReasonCode = new DerObjectIdentifier(“2.5.29.21”);
- DerObjectIdentifier SubjectAlternativeName = new DerObjectIdentifier(“2.5.29.17”);
- DerObjectIdentifier SubjectDirectoryAttributes = new DerObjectIdentifier(“2.5.29.9”);
- DerObjectIdentifier SubjectInfoAccess = new DerObjectIdentifier(“1.3.6.1.5.5.7.1.11”);
- DerObjectIdentifier SubjectKeyIdentifier = new DerObjectIdentifier(“2.5.29.14”);
- DerObjectIdentifier TargetInformation = new DerObjectIdentifier(“2.5.29.55”);
Key Usage values
Code from Bouncy Castle, but usage is documented in section 4.2.1.3 in the spec: https://tools.ietf.org/html/rfc5280#section-4.2.1.3
- int DigitalSignature = 128;
- int NonRepudiation = 64;
- int KeyEncipherment = 32;
- int DataEncipherment = 16;
- int KeyAgreement = 8;
- int KeyCertSign = 4;
- int CrlSign = 2;
- int EncipherOnly = 1;
- int DecipherOnly = 32768;