Skip to content

Instantly share code, notes, and snippets.

@Maverik
Last active August 4, 2020 10:56
Show Gist options
  • Save Maverik/5337717aadfb61b09e58 to your computer and use it in GitHub Desktop.
Save Maverik/5337717aadfb61b09e58 to your computer and use it in GitHub Desktop.
CSR Generator Snippet for Linqpad (C#)
// this snippet can be easily used in any normal c# project as well by simply removing the .Dump() methods
// and saving the output into a variable / file of your choice.
// you'll need to add BouncyCastle.Crypto nuget to use this.
// tested on 1.8.1
// OUTPUTS: CSR, Private Key, Public Key, Self-signed Certificate
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
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.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.X509.Extension;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using X509Extension = Org.BouncyCastle.Asn1.X509.X509Extension;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Utilities;
//The program generates three outputs named CSR, Private Key & Public Key
//Save each part in seperate files (leave the heading words out).
//Send the CSR to whomever you're getting the certificate from and save
//the private key until you get hold of the final certificate
//the new certificate & this private key will then combine to become
//final pfx. Please DO NOT lose private key as CSR & Private Key are
//a pair and one will not work without the other (and may result in monetory
//loss due to negligence!) Public key can largely be ignored. It's meant for
//cases where we don't use CA signed public certificates.
AsymmetricCipherKeyPair pair;
Pkcs10CertificationRequest csr;
Asn1SignatureFactory signatureFactory;
var random = new SecureRandom(new CryptoApiRandomGenerator());
var ecMode = false;
var values = new Dictionary<DerObjectIdentifier, string> {
{X509Name.CN, "Xero Compensator"}, //domain name inside the quotes
{X509Name.OU, "Infrastructure Team"},
{X509Name.O, "Backbone (UK) Limited"}, //Organisation's Legal name inside the quotes
{X509Name.L, "London"},
{X509Name.ST, "England"},
{X509Name.C, "GB"},
};
var subjectAlternateNames = new GeneralName[] { };
var extensions = new Dictionary<DerObjectIdentifier, X509Extension>()
{
{X509Extensions.BasicConstraints, new X509Extension(true, new DerOctetString(new BasicConstraints(false)))},
{X509Extensions.KeyUsage, new X509Extension(true, new DerOctetString(new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment | KeyUsage.DataEncipherment | KeyUsage.NonRepudiation)))},
{X509Extensions.ExtendedKeyUsage, new X509Extension(false, new DerOctetString(new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth)))},
};
if (values[X509Name.CN].StartsWith("www.")) values[X509Name.CN] = values[X509Name.CN].Substring(4);
if (!values[X509Name.CN].StartsWith("*.") && subjectAlternateNames.Length == 0)
subjectAlternateNames = new GeneralName[] { new GeneralName(GeneralName.DnsName, $"www.{values[X509Name.CN]}") };
if (subjectAlternateNames.Length > 0) extensions.Add(X509Extensions.SubjectAlternativeName, new X509Extension(false, new DerOctetString(new GeneralNames(subjectAlternateNames))));
var subject = new X509Name(values.Keys.Reverse().ToList(), values);
if (ecMode)
{
var gen = new ECKeyPairGenerator();
//secp256r1 combined with SHA256withECDSA minimum recommended as per NIST RFC5480 to achieve 128bit encryption
//secp384r1 combined with SHA384withECDSA message digest offers 192bit encryption
//secp521r1 combined with SHA512withECDSA message digest offers 256bit encryption (browsers are not supporting this option right now)
//browser cipher support can be checked via https://www.ssllabs.com/ssltest/viewMyClient.html
var ecp = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256r1");
gen.Init(new ECKeyGenerationParameters(new ECDomainParameters(ecp.Curve, ecp.G, ecp.N, ecp.H, ecp.GetSeed()), random));
pair = gen.GenerateKeyPair();
signatureFactory = new Asn1SignatureFactory("SHA256withECDSA", pair.Private);
extensions.Add(X509Extensions.SubjectKeyIdentifier, new X509Extension(false, new DerOctetString(new SubjectKeyIdentifierStructure(pair.Public))));
csr = new Pkcs10CertificationRequest(signatureFactory, subject, pair.Public, new DerSet(new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest, new DerSet(new X509Extensions(extensions)))), pair.Private);
}
else
{
var gen = new RsaKeyPairGenerator();
gen.Init(new KeyGenerationParameters(random, 2048));
pair = gen.GenerateKeyPair();
signatureFactory = new Asn1SignatureFactory("SHA256withRSA", pair.Private);
extensions.Add(X509Extensions.SubjectKeyIdentifier, new X509Extension(false, new DerOctetString(new SubjectKeyIdentifierStructure(pair.Public))));
csr = new Pkcs10CertificationRequest(signatureFactory, subject, pair.Public, new DerSet(new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest, new DerSet(new X509Extensions(extensions)))), pair.Private);
}
//Convert BouncyCastle csr to .PEM file.
var csrPem = new StringBuilder();
var csrPemWriter = new PemWriter(new StringWriter(csrPem));
csrPemWriter.WriteObject(csr);
csrPemWriter.Writer.Flush();
//Push the csr Text to a Label on a Page
csrPem.ToString().Dump("CSR");
//Convert BouncyCastle Private Key to .PEM file.
var privateKeyPem = new StringBuilder();
var privateKeyPemWriter = new PemWriter(new StringWriter(privateKeyPem));
privateKeyPemWriter.WriteObject(pair.Private);
privateKeyPemWriter.Writer.Flush();
//Push the privateKeyPem Text to a Label on a Page
privateKeyPem.ToString().Dump("Private Key");
//Convert BouncyCastle Public Key to .PEM file.
var publicKeyPem = new StringBuilder();
var publicKeyPemWriter = new PemWriter(new StringWriter(publicKeyPem));
publicKeyPemWriter.WriteObject(pair.Public);
publicKeyPemWriter.Writer.Flush();
//Push the publicKeyPem Text to a Label on a Page
publicKeyPem.ToString().Dump("Public Key");
//Generate a self signed x509 certificate from above
var notBefore = DateTime.UtcNow.Date;
var certGenerator = new X509V3CertificateGenerator();
certGenerator.SetSubjectDN(subject);
certGenerator.SetIssuerDN(subject);
certGenerator.SetNotBefore(notBefore);
certGenerator.SetNotAfter(notBefore.AddYears(1).AddSeconds(-1));
certGenerator.SetPublicKey(pair.Public);
certGenerator.SetSerialNumber(BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random));
foreach (var extension in extensions)
certGenerator.AddExtension(extension.Key, extension.Value.IsCritical, extension.Value.GetParsedValue());
var bouncyCert = certGenerator.Generate(signatureFactory);
var store = new Pkcs12Store();
var certificateEntry = new X509CertificateEntry(bouncyCert);
store.SetCertificateEntry(subject.ToString(), certificateEntry);
store.SetKeyEntry(subject.ToString(), new AsymmetricKeyEntry(pair.Private), new[] { certificateEntry });
using (var stream = new MemoryStream())
{
var tempPassword = "password";
store.Save(stream, tempPassword.ToCharArray(), random);
using (var cert = new X509Certificate2(stream.ToArray(), tempPassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable))
{
StringBuilder builder = new StringBuilder();
builder.AppendLine("-----BEGIN CERTIFICATE-----");
builder.AppendLine(Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks));
builder.AppendLine("-----END CERTIFICATE-----");
builder.ToString().Dump("Self-signed Certificate");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment