Exporting X.509 certificate WITHOUT private key

24,605

Solution 1

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.

Solution 2

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

Solution 3

There is an OpenSSL .NET wrapper you may find useful.

Share:
24,605
Aaronaught
Author by

Aaronaught

Software engineer at Google. Not an enterprise developer anymore. :-) Stuff I've used or currently use: JavaScript, jQuery, etc. AngularJS, Knockout, and similar MV* frameworks .NET/ASP.NET/MVC/Web API NServiceBus, RabbitMQ/EasyNetQ, pub/sub frameworks Selenium/WebDriver, SpecFlow, various BDD tools A bit of D3 A bit of Java Delphi SQL Server, SQL CE NoSQL (MongoDB, RavenDB, Redis, etc.) Oracle (although I try to forget) Web Services (REST, SOAP, WSE, WCF) Web Applications (MVC, MVVM, JS) Embedded Systems (HCxx, PIC, etc.), but that's ancient history Other things I know about: SOA and Distributed Systems Design Web security UI Design &amp; Data Visualization Project Management (Agile &amp; not-so-Agile) Software QA and test automation Doing tech talks that don't put everyone to sleep etc.

Updated on July 09, 2022

Comments

  • Aaronaught
    Aaronaught almost 2 years

    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?

  • RRR
    RRR over 12 years
    do you know if there is a way to export only the private key without the whole certificate?, I have to extricate the private key as byte array, and I dont find any way to do it....
  • Aaronaught
    Aaronaught over 12 years
    @RRR: Whatever it is that you're trying to do, I'd advise against it because the "private key" of a certificate is a lot more than just a byte array, it's a cryptographic algorithm, specifically an AsymmetricAlgorithm, and different certificates may have completely different algorithms. If you lose this information, it will be very difficult to reconstruct and decrypt/verify anything encrypted/signed by the public key. If you really want to try to mess with it, look at X509Certificate2.PrivateKey and work from there.
  • Jim Flood
    Jim Flood over 10 years
    @Aaronaught: You generally never want to export the private key along with the certificate. The private key must remain a closely held secret. You can verify anything signed with the private key having only the certificate -- certificates only contain the public key, and this is all that is needed to verify a signature. You generally do not want to use the private key to encrypt data. Also, the private and public keys are not interchangeable -- given a public key it is close to impossible to guess the private key, but not vice versa. So, keep that private key at home.
  • Raven
    Raven over 6 years
    Note that this yields a different file format, so its no longer stored as a .pfx/.p12 file.