Skip to content

Instantly share code, notes, and snippets.

@dealproc
Last active August 29, 2015 14:23
Show Gist options
  • Save dealproc/f20882d0defb9acbdf4a to your computer and use it in GitHub Desktop.
Save dealproc/f20882d0defb9acbdf4a to your computer and use it in GitHub Desktop.
Issuing a Self-Signed certificate

Depends on using BouncyCastle nuget package for the certificate generation.

using Mono.Options;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using System;
using System.Security.Cryptography.X509Certificates;
namespace testssl {
class Program {
//http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx
//http://www.wiktorzychla.com/2012/12/how-to-create-x509certificate2.html
static CertificateCategory _toCreate = CertificateCategory.Unknown;
static string _certSubject = "";
static string _certIssuer = "";
static string _privateKeyFile = "";
static string _fileNameBase = "";
static string _pksFilePassword = "";
static bool show_help = false;
static int Main(string[] args) {
var x = ParseParameters(args);
if (x != 0) { return x; }
AsymmetricCipherKeyPair myCAKey = null;
switch (_toCreate) {
case CertificateCategory.ClientCert:
Console.WriteLine("Opening .key file.");
myCAKey = Certificates.Import(_privateKeyFile);
Console.WriteLine("Generating Client Certificate");
var clientCert = Certificates.ClientCertificate.Generate(_certSubject.EnsureCNPrefix(), _certIssuer.EnsureCNPrefix(), myCAKey.Private);
var finalFileName = _fileNameBase + "-client.pfx";
Console.WriteLine("Saving to: " + finalFileName);
Certificates.SaveToFile(DotNetUtilities.FromX509Certificate(clientCert), myCAKey, finalFileName, _certSubject, _pksFilePassword);
break;
case CertificateCategory.TrustedCA:
Console.WriteLine("Generating Root CA and Key");
X509Certificate2 rootCA = Certificates.CertificateAuthority.Generate(_certSubject.EnsureCNPrefix(), ref myCAKey);
Console.WriteLine("Saving CA Certificate");
Certificates.SaveToFile(DotNetUtilities.FromX509Certificate(rootCA), myCAKey, _fileNameBase + ".pfx", _certSubject, _pksFilePassword);
Console.WriteLine("Saving *.key file");
Certificates.ExportKey(myCAKey, _fileNameBase + ".key");
break;
default:
Console.WriteLine("Unknown command.");
return -1;
}
Console.WriteLine("done!");
#if DEBUG
Console.ReadLine();
#endif
return 0;
}
private static int ParseParameters(string[] args) {
var p = new OptionSet{
{
"t|type=" , "The type of certificate to generate: (CA/Client)", v => {
switch (v.ToLower()) {
case "ca":
_toCreate = CertificateCategory.TrustedCA;
break;
case "client":
_toCreate = CertificateCategory.ClientCert;
break;
}
}
},
{ "p|password=", "The password for the pfx file.", v => _pksFilePassword = v },
{ "s|subject=", "The subject of the certificate", v => _certSubject = v },
{ "i|issuer=", "The CA Issuer (for client certificates)", v => _certIssuer = v },
{ "k|key=", "The signing key file (for client certificates)", v => _privateKeyFile = v },
{ "f|file=", "The output file, minus the extension (we add that for you)", v => _fileNameBase = v },
{ "h|help", "Show this message and exit", v => show_help = v != null }
};
try {
p.Parse(args);
} catch (OptionException e) {
Console.WriteLine("testssl: ");
Console.WriteLine(e.Message);
Console.WriteLine("Try `testssl --help ` for more information.");
return -1;
}
return 0;
}
}
}
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.Parameters;
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;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
namespace testssl {
public static class Certificates {
/// <summary>
/// Saves a bouncycastle certificate to a pfx file, with the private key.
/// </summary>
/// <param name="newCert"></param>
/// <param name="kp"></param>
/// <param name="fileStream"></param>
/// <param name="CertAlias"></param>
/// <param name="Password"></param>
public static void SaveToFile(Org.BouncyCastle.X509.X509Certificate newCert, AsymmetricCipherKeyPair kp, string FilePath, string CertAlias, string Password) {
using (var certFile = File.Create(FilePath)) {
SaveToFile(newCert, kp, certFile, CertAlias, Password);
}
}
/// <summary>
/// Saves a bouncycastle certificate to a stream, with the private key.
/// <remarks>Useful for unit testing scenarios</remarks>
/// </summary>
/// <param name="newCert"></param>
/// <param name="kp"></param>
/// <param name="fileStream"></param>
/// <param name="CertAlias"></param>
/// <param name="Password"></param>
public static void SaveToFile(Org.BouncyCastle.X509.X509Certificate newCert, AsymmetricCipherKeyPair kp, Stream fileStream, string CertAlias, string Password) {
var newStore = new Pkcs12Store();
var certEntry = new X509CertificateEntry(newCert);
newStore.SetCertificateEntry(CertAlias, certEntry);
newStore.SetKeyEntry(CertAlias, new AsymmetricKeyEntry(kp.Private), new[] { certEntry });
newStore.Save(fileStream, Password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator()));
}
/// <summary>
/// Reads a pfx file into memory, converting it to a bouncycastle certificate.
/// </summary>
/// <param name="FilePath"></param>
/// <param name="password"></param>
/// <returns></returns>
public static Org.BouncyCastle.X509.X509Certificate OpenFromFile(string FilePath, string password) {
return DotNetUtilities.FromX509Certificate(new X509Certificate2(FilePath, password));
}
/// <summary>
/// Reads a pfx stream, converting it to a bouncycastle certificate.
/// </summary>
/// <param name="stream"></param>
/// <param name="password"></param>
/// <returns></returns>
public static Org.BouncyCastle.X509.X509Certificate OpenFromFile(Stream stream, string password) {
return DotNetUtilities.FromX509Certificate(new X509Certificate2(ReadStream(stream), password));
}
public static AsymmetricCipherKeyPair Generate(int strength = 2048) {
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
var keyGenerationParameters = new KeyGenerationParameters(random, strength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
return keyPairGenerator.GenerateKeyPair();
}
public static void ExportKey(AsymmetricCipherKeyPair myCAKey, string fileName) {
using (var fStream = File.OpenWrite(fileName)) {
ExportKey(myCAKey, fStream);
}
}
public static void ExportKey(AsymmetricCipherKeyPair myCAKey, Stream s) {
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(myCAKey.Private);
TextWriter tw = new StreamWriter(s);
PemWriter pw = new PemWriter(tw);
pw.WriteObject(myCAKey.Private);
pw.Writer.Flush();
}
public static AsymmetricCipherKeyPair Import(string fileName) {
using (var fStream = File.Open(fileName, FileMode.Open)) {
return Import(fStream);
}
}
public static AsymmetricCipherKeyPair Import(Stream s) {
using (TextReader tr = new StreamReader(s)) {
PemReader pr = new PemReader(tr);
return (AsymmetricCipherKeyPair)pr.ReadObject();
}
}
//http://stackoverflow.com/questions/844997/encrypting-a-bouncycastle-rsa-key-pair-and-storing-in-a-sql2008-database
public static bool addCertToStore(System.Security.Cryptography.X509Certificates.X509Certificate2 cert, StoreName st, StoreLocation sl) {
bool bRet = false;
try {
X509Store store = new X509Store(st, sl);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
} catch {
}
return bRet;
}
public static X509Certificate2 getCertFromStore(StoreName st, StoreLocation sl, string issuerName) {
try {
X509Store store = new X509Store(st, sl);
store.Open(OpenFlags.OpenExistingOnly);
var certCollection = store.Certificates.Find(X509FindType.FindByIssuerName, issuerName, false);
if (certCollection.Count > 0) {
return certCollection[0];
}
} catch {
}
return null;
}
private static byte[] ReadStream(System.IO.Stream input) {
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream()) {
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0) {
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
public static class ClientCertificate {
public static X509Certificate2 Generate(string subjectName, string issuerName, AsymmetricKeyParameter issuerPrivKey) {
const int keyStrength = 2048;
// Generating Random Numbers
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
// The Certificate Generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// Serial Number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
// Signature Algorithm
const string signatureAlgorithm = "SHA256WithRSA";
certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);
// Issuer and Subject Name
X509Name subjectDN = new X509Name(subjectName);
X509Name issuerDN = new X509Name(issuerName);
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
// Valid For
DateTime notBefore = DateTime.UtcNow.Date;
DateTime notAfter = notBefore.AddYears(2);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// Subject Public Key
AsymmetricCipherKeyPair subjectKeyPair;
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Generating the Certificate
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;
// selfsign certificate
Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(issuerPrivKey, random);
// correcponding private key
PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
// merge into X509Certificate2
X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());
Asn1Sequence seq = (Asn1Sequence)Asn1Object.FromByteArray(info.PrivateKey.GetDerEncoded());
if (seq.Count != 9) {
//throw new PemException("malformed sequence in RSA private key");
}
RsaPrivateKeyStructure rsa = new RsaPrivateKeyStructure(seq);
RsaPrivateCrtKeyParameters rsaparams = new RsaPrivateCrtKeyParameters(
rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, rsa.Exponent2, rsa.Coefficient);
x509.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
return x509;
}
}
public static class CertificateAuthority {
public static X509Certificate2 Generate(string subjectName, ref AsymmetricCipherKeyPair CaPrivateKey) {
const int keyStrength = 2048;
// Generating Random Numbers
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
// The Certificate Generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
// Serial Number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
// Signature Algorithm
const string signatureAlgorithm = "SHA256WithRSA";
certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);
// Issuer and Subject Name
X509Name subjectDN = new X509Name(subjectName);
X509Name issuerDN = subjectDN;
certificateGenerator.SetIssuerDN(issuerDN);
certificateGenerator.SetSubjectDN(subjectDN);
// Valid For
DateTime notBefore = DateTime.UtcNow.Date;
DateTime notAfter = notBefore.AddYears(10);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
// Subject Public Key
AsymmetricCipherKeyPair subjectKeyPair;
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Generating the Certificate
AsymmetricCipherKeyPair issuerKeyPair = subjectKeyPair;
// selfsign certificate
Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(issuerKeyPair.Private, random);
X509Certificate2 x509 = new System.Security.Cryptography.X509Certificates.X509Certificate2(certificate.GetEncoded());
CaPrivateKey = issuerKeyPair;
return x509;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment