X509 compatible certificates are commonly used in various scenarios. Creating a new certificate usually involves using the makecert.exe or a specialized application (I prefer Portecle, it’s easy to use and free).
Creating certificates programmatically is also a common requirement. Although the base class library supports X509 infrastructure (System.Security.Cryptography.X509Certificates namespace), there is no easy way to create certificates with sole BCL. Fortunately, there is Bouncy Castle which contains all required APIs. Bouncy Castle is available on NuGet, adding it to your solution could just not be easier.
The code below is based on a Stack Overflow entry followed by a comment from someone.
/// <summary>
/// Based on:
/// http://stackoverflow.com/questions/3770233/is-it-possible-to-programmatically-generate-an-x509-certificate-using-only-c
/// http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx
/// </summary>
public class CertificateGenerator
{
public static X509Certificate
GenerateCertificate( string subjectName, out AsymmetricCipherKeyPair kp )
{
var kpgen = new RsaKeyPairGenerator();
// certificate strength 1024 bits
kpgen.Init( new KeyGenerationParameters(
new SecureRandom( new CryptoApiRandomGenerator() ), 1024 ) );
kp = kpgen.GenerateKeyPair();
var gen = new X509V3CertificateGenerator();
var certName = new X509Name( "CN=" + subjectName );
var serialNo = BigInteger.ProbablePrime( 120, new Random() );
gen.SetSerialNumber( serialNo );
gen.SetSubjectDN( certName );
gen.SetIssuerDN( certName );
gen.SetNotAfter( DateTime.Now.AddYears( 100 ) );
gen.SetNotBefore( DateTime.Now.Subtract( new TimeSpan( 7, 0, 0, 0 ) ) );
gen.SetSignatureAlgorithm( "SHA1withRSA" );
gen.SetPublicKey( kp.Public );
gen.AddExtension(
X509Extensions.AuthorityKeyIdentifier.Id,
false,
new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo( kp.Public ),
new GeneralNames( new GeneralName( certName ) ),
serialNo ) );
/*
1.3.6.1.5.5.7.3.1 - id_kp_serverAuth
1.3.6.1.5.5.7.3.2 - id_kp_clientAuth
1.3.6.1.5.5.7.3.3 - id_kp_codeSigning
1.3.6.1.5.5.7.3.4 - id_kp_emailProtection
1.3.6.1.5.5.7.3.5 - id-kp-ipsecEndSystem
1.3.6.1.5.5.7.3.6 - id-kp-ipsecTunnel
1.3.6.1.5.5.7.3.7 - id-kp-ipsecUser
1.3.6.1.5.5.7.3.8 - id_kp_timeStamping
1.3.6.1.5.5.7.3.9 - OCSPSigning
*/
gen.AddExtension(
X509Extensions.ExtendedKeyUsage.Id,
false,
new ExtendedKeyUsage( new [] { KeyPurposeID.IdKPServerAuth } ) );
var newCert = gen.Generate( kp.Private );
return newCert;
}
public static void SaveToFile(
X509Certificate newCert,
AsymmetricCipherKeyPair kp,
string FilePath,
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 }
);
using ( var certFile = File.Create( FilePath ) )
{
newStore.Save(
certFile,
Password.ToCharArray(),
new SecureRandom( new CryptoApiRandomGenerator() )
);
}
}
}
I can now link this with the BCL as the saved certificate is compatible with the System.Security.Cryptography.X509Certificates.X509Certificate2:
// create bouncy castle cert and save it
AsymmetricCipherKeyPair kp;
var x509 = CertificateGenerator.GenerateCertificate( "Subject", out kp );
string FilePath = "cert.pfx";
string Alias = "foo";
string Pwd = "bar";
CertificateGenerator.SaveToFile( x509, kp, FilePath, Alias, Pwd );
// open the store as X509Certificate2
var x5092 = new System.Security.Cryptography.X509Certificates.X509Certificate2( FilePath, Pwd );
Console.WriteLine( x5092.SubjectName );
Console.WriteLine( x5092.Thumbprint );
Console.WriteLine( x5092.PrivateKey.SignatureAlgorithm );
6 comments:
Ok, I'll bite. What's BCL?
http://en.wikipedia.org/wiki/Base_Class_Library
Oh, haha. Duh, I get it now.
Thank you so much - I had had enough of PInvoke
How to generate root certificate (CA)? Thanks
Nice Article, this what I want.
Thanks Wiktor !!!
Post a Comment