[ACCEPTED]-Exporting X.509 certificate WITHOUT private key-x509certificate2

Accepted answer
Score: 20

For anyone else who might have stumbled 11 on this, I figured it out. If you specify 10 X509ContentType.Cert as the first (and only) parameter to X509Certificate.Export, it 9 only exports the public key. On the other 8 hand, specifying X509ContentType.Pfx includes the private key 7 if one exists.

I could have sworn that I 6 was seeing different behaviour last week, but 5 I must have already had the private key 4 installed when I was testing. When I deleted 3 that certificate today and started again 2 from scratch, I saw that there was no private 1 key in the exported cert.

Score: 11

I found the following program helpful for 5 reassuring myself that the RawData property of 4 the certificate contains only the public 3 key (MSDN is unclear on this), and that 2 the answer above regarding X509ContentType.Cert vs. X509ContentType.Pfx works as 1 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
Score: 1

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

0

More Related questions