public static X509Certificate2 CreateCertificate(
string storeType,
string storePath,
string password,
string applicationUri,
string applicationName,
string subjectName,
IList<String> domainNames,
ushort keySize,
DateTime startTime,
ushort lifetimeInMonths,
ushort hashSizeInBits,
bool isCA,
X509Certificate2 issuerCAKeyCert)
{
if (!String.IsNullOrEmpty(storeType) &&
storeType != CertificateStoreType.Directory)
{
throw new NotSupportedException("Cannot create a certificate for a non directory store.");
}
if (issuerCAKeyCert != null)
{
if (!issuerCAKeyCert.HasPrivateKey)
{
throw new NotSupportedException("Cannot sign with a CA certificate without a private key.");
}
throw new NotSupportedException("Signing with an issuer CA certificate is currently unsupported.");
}
// set default values.
SetSuitableDefaults(
ref applicationUri,
ref applicationName,
ref subjectName,
ref domainNames,
ref keySize,
ref lifetimeInMonths,
isCA);
// cert generators
SecureRandom random = new SecureRandom();
X509V3CertificateGenerator cg = new X509V3CertificateGenerator();
// Serial Number
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
cg.SetSerialNumber(serialNumber);
// build name attributes
var nameOids = new ArrayList();
nameOids.Add(X509Name.DC);
nameOids.Add(X509Name.CN);
var nameValues = new ArrayList();
nameValues.Add(domainNames[0]);
nameValues.Add(applicationName);
// self signed
X509Name subjectDN = new X509Name(nameOids, nameValues);
X509Name issuerDN = subjectDN;
cg.SetIssuerDN(issuerDN);
cg.SetSubjectDN(subjectDN);
// valid for
cg.SetNotBefore(startTime);
cg.SetNotAfter(startTime.AddMonths(lifetimeInMonths));
// Private/Public Key
AsymmetricCipherKeyPair subjectKeyPair;
var keyGenerationParameters = new KeyGenerationParameters(random, keySize);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
subjectKeyPair = keyPairGenerator.GenerateKeyPair();
cg.SetPublicKey(subjectKeyPair.Public);
// add extensions
// Subject key identifier
cg.AddExtension(X509Extensions.SubjectKeyIdentifier.Id, false,
new SubjectKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public)));
// Basic constraints
cg.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCA));
// Authority Key identifier
var issuerKeyPair = subjectKeyPair;
var issuerSerialNumber = serialNumber;
cg.AddExtension(X509Extensions.AuthorityKeyIdentifier.Id, false,
new AuthorityKeyIdentifier(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public),
new GeneralNames(new GeneralName(issuerDN)), issuerSerialNumber));
if (!isCA)
{
// Key usage
cg.AddExtension(X509Extensions.KeyUsage, true,
new KeyUsage(KeyUsage.DataEncipherment | KeyUsage.DigitalSignature |
KeyUsage.NonRepudiation | KeyUsage.KeyCertSign | KeyUsage.KeyEncipherment));
// Extended Key usage
cg.AddExtension(X509Extensions.ExtendedKeyUsage, true,
new ExtendedKeyUsage(new List<DerObjectIdentifier>() {
new DerObjectIdentifier("1.3.6.1.5.5.7.3.1"), // server auth
new DerObjectIdentifier("1.3.6.1.5.5.7.3.2"), // client auth
}));
// subject alternate name
cg.AddExtension(X509Extensions.SubjectAlternativeName, false,
new GeneralNames(new GeneralName[] {
new GeneralName(GeneralName.UniformResourceIdentifier, applicationUri),
new GeneralName(GeneralName.DnsName, domainNames[0])}));
}
else
{
// Key usage CA
cg.AddExtension(X509Extensions.KeyUsage, true,
new KeyUsage(KeyUsage.CrlSign | KeyUsage.DigitalSignature | KeyUsage.KeyCertSign));
}
// sign certificate
ISignatureFactory signatureFactory =
new Asn1SignatureFactory((hashSizeInBits < 256) ? "SHA1WITHRSA" : "SHA256WITHRSA", subjectKeyPair.Private, random);
Org.BouncyCastle.X509.X509Certificate x509 = cg.Generate(signatureFactory);
// create pkcs12 store for cert and private key
X509Certificate2 certificate = null;
using (MemoryStream pfxData = new MemoryStream())
{
Pkcs12Store pkcsStore = new Pkcs12StoreBuilder().Build();
X509CertificateEntry[] chain = new X509CertificateEntry[1];
string passcode = "passcode";
chain[0] = new X509CertificateEntry(x509);
pkcsStore.SetKeyEntry(applicationName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain);
pkcsStore.Save(pfxData, passcode.ToCharArray(), random);
// merge into X509Certificate2
certificate = CreateCertificateFromPKCS12(pfxData.ToArray(), passcode);
}
Utils.Trace(Utils.TraceMasks.Security, "Created new certificate: {0}", certificate.Thumbprint);
// add cert to the store.
if (!String.IsNullOrEmpty(storePath))
{
ICertificateStore store = null;
if (storeType == CertificateStoreType.Directory)
{
using (store = new DirectoryCertificateStore())
{
store.Open(storePath);
store.Add(certificate);
store.Close();
}
}
}
// note: this cert has a private key!
return certificate;
}