We're trying to generate an X509 certificate (including the private key) programmatically using C# and the BouncyCastle library. We've tried using some of the code from this sample by Felix Kollmann but the private key part of the certificate returns null. Code and unit test are as below:
using System; using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; namespace MyApp { public class CertificateGenerator { /// <summary> /// /// </summary> /// <remarks>Based on <see cref="http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx"/></remarks> /// <param name="subjectName"></param> /// <returns></returns> public static byte[] GenerateCertificate(string subjectName) { var kpgen = new RsaKeyPairGenerator(); kpgen.Init(new KeyGenerationParameters(new SecureRandom(new CryptoApiRandomGenerator()), 1024)); var 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("MD5WithRSA"); gen.SetPublicKey(kp.Public); gen.AddExtension( X509Extensions.AuthorityKeyIdentifier.Id, false, new AuthorityKeyIdentifier( SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public), new GeneralNames(new GeneralName(certName)), serialNo)); gen.AddExtension( X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(new ArrayList() { new DerObjectIdentifier("1.3.6.1.5.5.7.3.1") })); var newCert = gen.Generate(kp.Private); return DotNetUtilities.ToX509Certificate(newCert).Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password"); } } }
Unit test:
using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MyApp { [TestClass] public class CertificateGeneratorTests { [TestMethod] public void GenerateCertificate_Test_ValidCertificate() { // Arrange string subjectName = "test"; // Act byte[] actual = CertificateGenerator.GenerateCertificate(subjectName); // Assert var cert = new X509Certificate2(actual, "password"); Assert.AreEqual("CN=" + subjectName, cert.Subject); Assert.IsInstanceOfType(cert.PrivateKey, typeof(RSACryptoServiceProvider)); } } }
Open cmd prompt, change directory to desktop & type command- openssl. It is a process of creating a simple x509 certificate that will be used for digital signatures. Press enter and fill in all the required information like the password for creating keys & a few personal information.
Cryptography. X509Certificates namespace contains the common language runtime implementation of the Authenticode X. 509 v. 3 certificate. This certificate is signed with a private key that uniquely and positively identifies the holder of the certificate.
No matter its intended application(s), each X. 509 certificate includes a public key, digital signature, and information about both the identity associated with the certificate and its issuing certificate authority (CA): The public key is part of a key pair that also includes a private key.
Just to clarify, an X.509 certificate does not contain the private key. The word certificate is sometimes misused to represent the combination of the certificate and the private key, but they are two distinct entities. The whole point of using certificates is to send them more or less openly, without sending the private key, which must be kept secret. An X509Certificate2
object may have a private key associated with it (via its PrivateKey
property), but that's only a convenience as part of the design of this class.
In your first BouncyCastle code example, newCert
is really just the certificate and DotNetUtilities.ToX509Certificate(newCert)
is built from the certificate only.
Considering that the PKCS#12 format requires the presence of a private key, I'm quite surprised that the following part even works (considering you're calling it on a certificate which can't possibly know the private key):
.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "password");
(gen.Generate(kp.Private)
signs the certificate using the private key, but doesn't put the private key in the certificate, which wouldn't make sense.)
If you want your method to return both the certificate and the private key you could either:
X509Certificate2
object in which you've initialized the PrivateKey
propertybyte[]
content (as if it was a file). Step 3 in the link you've sent (mirror) explains how to build a PKCS#12 store.Returning the byte[]
(DER) structure for the X.509 certificate itself will not contain the private key.
If your main concern (according to your test case) is to check that the certificate was built from an RSA key-pair, you can check the type of its public key instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With