Bouncy Castle C# is a great project, making X509 more accessible to programmers 
working on all kinds of systems. Versatile enough that a compile-time warning 
about an unused extension would be an annoying false positive for some. Chris 
asked me to share the code I wrote. So as to increase the number of example 
usages of Bouncy Castle C# available publicly on the Internet, here you go: a 
first encounter with Bouncy Castle.

Feel free to copy and improve upon the attached example source code.
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.X509.Extension;
namespace UnicornCommon
{
    public class Certificate
    {
        /// <summary>
        /// Generate a new certificate.
        /// </summary>
        /// <param name="name">The Common Name of the receiver of the 
certificate. For example *.utm.is.</param>
        /// <param name="expiryDate">The date when the new certificate is to 
expire. Either a week from now or decades from now.</param>
        /// <param name="passwd">The password used to unlock the  root 
certificate.</param>
        /// <example>
        /// var cert = new Certificate("*.customer.example",
        /// DateTime.UtcNow.addWeeks(1), "***");
        /// using (var memory = new MemoryStream(1024))
        ///     cert.SavePrivateKey(memory);
        /// </example>
        private static readonly string certificateAuthority = 
System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/your-path-to/SHA-256.private.pem");
        private static readonly string certificateAuthorityPublic = 
System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/your-path-to/SHA-256.cer");
        System.Security.Cryptography.X509Certificates.X509Certificate2 keypair;
        private const string issuerName = "(Your Company Name)";
        private const string signatureHashAlgorithm = "SHA512WithRSA";
        private const int certificateStrength = 2048;

        private static readonly log4net.ILog logger = 
log4net.LogManager.GetLogger
            (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public MemoryStream Stream()
        {
            Byte[] bytes = keypair.Export
                
(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, 
"dummypassword");
            return new MemoryStream(bytes);
        }


        private class Keyring : IPasswordFinder
        {
            char[] password;
            public Keyring(string passwd)
            {
                password = passwd.ToCharArray();
            }
            public char[] GetPassword()
            {
                return password;
            }
        }


        private static X509Certificate ReadPubKey(string filename)
        {
            var reader = File.OpenText(filename);
            X509Certificate certificate;

            try
            {
                var pemReader = new PemReader(reader);
                certificate = (X509Certificate)pemReader.ReadObject();
            }

            catch (PemException e)
            {
                //Definitely corruption
                logger.Error("Public key corrupt.");
                throw;
            }

            return certificate;
        }

        private static AsymmetricKeyParameter ReadPrivKey(string filename, 
string passwd)
        {
            var reader = File.OpenText(filename);
            Keyring keyring = new Keyring(passwd);
            AsymmetricKeyParameter keyPair;

            try
            {
                var pemReader = new PemReader(reader, keyring);
                keyPair = (AsymmetricKeyParameter)pemReader.ReadObject();
            }

            catch (PemException e)
            {
                //Either corruption or, more likely, an incorrect password
                if(passwd != null)
                    logger.Warn("Incorrect password to private key (or private 
key corrupt).");
                throw;
            }

            return keyPair;
        }


        private static AsymmetricCipherKeyPair GenKeyPair(SecureRandom random)
       {
            var keyGenerationParameters = new KeyGenerationParameters(random, 
certificateStrength);
            var keyGenerator = new RsaKeyPairGenerator();
            keyGenerator.Init(keyGenerationParameters);
            var keyPair = keyGenerator.GenerateKeyPair();
            return keyPair;
        }

        private static void SetSerialNumber(X509V3CertificateGenerator certGen, 
SecureRandom random)
        {
            var serialNumber = BigIntegers.CreateRandomInRange
               (BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random)
            ;
            certGen.SetSerialNumber(serialNumber);
        }

        private void SetExpiryDate(X509V3CertificateGenerator certGen, DateTime 
expiryDate)
        {
            certGen.SetNotBefore(DateTime.UtcNow.Date);
            certGen.SetNotAfter(expiryDate);
        }

        public Certificate(string clientName, DateTime expiryDate, string 
passwd)
        {
            var certGen = new X509V3CertificateGenerator();
            certGen.SetSubjectDN(new X509Name("CN=" + clientName));
            certGen.SetIssuerDN(new X509Name("CN=(Your CA Name)"));
            var random = new SecureRandom(new CryptoApiRandomGenerator());
            SetSerialNumber(certGen, random);
            SetExpiryDate(certGen, expiryDate);

            var keyPair = GenKeyPair(random);
            certGen.SetPublicKey(keyPair.Public);
            certGen.SetSignatureAlgorithm(signatureHashAlgorithm);
            const int PROVIDER_RSA_AES = 24;
            CspParameters cspParameters;
            cspParameters = new CspParameters(PROVIDER_RSA_AES);
            //cspParameters.Flags = CspProviderFlags.CreateEphemeralKey;
            cspParameters.KeyNumber = (int)KeyNumber.Exchange;
            cspParameters.KeyContainerName = "(YourContainerName)";

           X509Certificate rootCert = ReadPubKey(certificateAuthorityPublic);
            var authorityKeyIdentifier = new 
AuthorityKeyIdentifierStructure(rootCert);
            certGen.AddExtension(
                X509Extensions.AuthorityKeyIdentifier.Id, false, 
authorityKeyIdentifier);

            AsymmetricKeyParameter rootPriv = ReadPrivKey(certificateAuthority, 
passwd);

            X509Certificate x509 = certGen.Generate(rootPriv, random);
            keypair = new 
System.Security.Cryptography.X509Certificates.X509Certificate2(DotNetUtilities.ToX509Certificate(x509));
            var BCprivKey = keyPair.Private as RsaPrivateCrtKeyParameters ;
            var DNprivKey = DotNetUtilities.ToRSAParameters(BCprivKey);
            var rsaProvider = new 
System.Security.Cryptography.RSACryptoServiceProvider(1024, cspParameters);
            rsaProvider.ImportParameters(DNprivKey);
            keypair.PrivateKey = rsaProvider as AsymmetricAlgorithm;
        }
    }
}

Reply via email to