I thought this would be straightforward but apparently it isn't. I have a certificate installed that has a private key, exportable, and I want to programmatically export it with the public key ONLY. In other words, I want a result equivalent to selecting "Do not export the private key" when exporting through certmgr and exporting to .CER.
It seems that all of the X509Certificate2.Export methods will export the private key if it exists, as PKCS #12, which is the opposite of what I want.
Is there any way using C# to accomplish this, or do I need to start digging into CAPICOM?
Export the certificate in PFX format Windows doesn't store the private key in a separate file. you can't export the private key alone. If you need to export the private key from either MMC or IIS, you should export the certificate in . pfx (PKCS#12) file format along with the private key.
Start the Certificates snap-in. In the snap-in's console tree, expand the Personal container, and click the Certificates subcontainer. In the details pane, right-click the certificate you want to export, select All Tasks, and then click Export… This action will start the Certificate Export Wizard.
An X. 509 certificate consists of two keys, namely a public key and a private key. This key pair, depending upon the application, allows you to sign documents using the private key so that the intended person can verify the signature using the public key related to it.
A missing private key could mean: The certificate is not being installed on the same server that generated the CSR. The pending request was deleted from IIS. The certificate was installed through the Certificate Import Wizard rather than through IIS.
For anyone else who might have stumbled on this, I figured it out. If you specify X509ContentType.Cert
as the first (and only) parameter to X509Certificate.Export
, it only exports the public key. On the other hand, specifying X509ContentType.Pfx
includes the private key if one exists.
I could have sworn that I was seeing different behaviour last week, but I must have already had the private key installed when I was testing. When I deleted that certificate today and started again from scratch, I saw that there was no private key in the exported cert.
I found the following program helpful for reassuring myself that the RawData
property of the certificate contains only the public key (MSDN is unclear on this), and that the answer above regarding X509ContentType.Cert
vs. X509ContentType.Pfx
works as expected:
using System;
using System.Linq;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;
class Program
{
static void Main( string[] args )
{
var certPath = @"C:\blah\somecert.pfx";
var certPassword = "somepassword";
var orig = new X509Certificate2( certPath, certPassword, X509KeyStorageFlags.Exportable );
Console.WriteLine( "Orig : RawData.Length = {0}, HasPrivateKey = {1}", orig.RawData.Length, orig.HasPrivateKey );
var certBytes = orig.Export( X509ContentType.Cert );
var certA = new X509Certificate2( certBytes );
Console.WriteLine( "cert A : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certA.RawData.Length, certA.HasPrivateKey, certBytes.Length );
// NOTE that this the only place the byte count differs from the others
certBytes = orig.Export( X509ContentType.Pfx );
var certB = new X509Certificate2( certBytes );
Console.WriteLine( "cert B : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certB.RawData.Length, certB.HasPrivateKey, certBytes.Length );
var keyIdentifier = ( new X509SecurityToken( orig ) ).CreateKeyIdentifierClause<X509RawDataKeyIdentifierClause>();
certBytes = keyIdentifier.GetX509RawData();
var certC = new X509Certificate2( certBytes );
Console.WriteLine( "cert C : RawData.Length = {0}, HasPrivateKey = {1}, certBytes.Length = {2}", certC.RawData.Length, certC.HasPrivateKey, certBytes.Length );
Console.WriteLine( "RawData equals original RawData: {0}", certC.RawData.SequenceEqual( orig.RawData ) );
Console.ReadLine();
}
}
It outputs the following:
Orig : RawData.Length = 1337, HasPrivateKey = True cert A : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337 cert B : RawData.Length = 1337, HasPrivateKey = True, certBytes.Length = 3187 cert C : RawData.Length = 1337, HasPrivateKey = False, certBytes.Length = 1337 RawData equals original RawData: True
There is an OpenSSL .NET wrapper you may find useful.
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