6

I am currently able to extract a private key from a PFX file using OpenSSL using the following commands:

openssl pkcs12 -in filename.pfx -nocerts -out privateKey.pem

openssl.exe rsa -in privateKey.pem -out private.pem

The private.pem file begins with ---BEGIN RSA PRIVATE KEY--- and ends with ---END RSA PRIVATE KEY---

I want to do the same in C# using .NET libraries or the Bouncy Castle library.

How do I do this?

p.campbell
  • 98,673
  • 67
  • 256
  • 322
Subbu
  • 839
  • 2
  • 12
  • 34
  • Similar questions: http://stackoverflow.com/questions/3826321/how-can-constructing-an-x509certificate2-from-a-pkcs12-byte-array-throw-cryptogr http://stackoverflow.com/questions/5471716/how-to-get-private-key-from-pkcs12-p12-file-using-c – Andrew Savinykh Jun 06 '11 at 22:48

4 Answers4

8

This is what worked for me. Should also work for you:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;

namespace SO6258771
{
    class Program
    {
        static void Main()
        {
            // Load your certificate from file
            X509Certificate2 certificate = new X509Certificate2("filename.pfx", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

            // Now you have your private key in binary form as you wanted
            // You can use rsa.ExportParameters() or rsa.ExportCspBlob() to get you bytes
            // depending on format you need them in
            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;

            // Just for lulz, let's write out the PEM representation of the private key
            // using Bouncy Castle, so that we are 100% sure that the result is exaclty the same as:
            // openssl pkcs12 -in filename.pfx -nocerts -out privateKey.pem
            // openssl.exe rsa -in privateKey.pem -out private.pem

            // You should of course dispose of / close the streams properly. I'm skipping this part for brevity
            MemoryStream memoryStream = new MemoryStream();
            TextWriter streamWriter = new StreamWriter(memoryStream);
            PemWriter pemWriter = new PemWriter(streamWriter);

            AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(rsa);
            pemWriter.WriteObject(keyPair.Private);
            streamWriter.Flush();

            // Here is the output with ---BEGIN RSA PRIVATE KEY---
            // that should be exactly the same as in private.pem
            Console.Write(Encoding.ASCII.GetString(memoryStream.GetBuffer()));
        }
    }
}
Andrew Savinykh
  • 25,351
  • 17
  • 103
  • 158
  • The WriteObject method in my build environment is underlined in red in my vs2008 editor. The error says : Error 1 The best overloaded method match for 'Org.BouncyCastle.Utilities.IO.Pem.PemWriter.WriteObject(Org.BouncyCastle.Utilities.IO.Pem.PemObjectGenerator)' has some invalid arguments. Whats wrong here? – Subbu Jun 07 '11 at 16:46
  • 3
    It is crucial that you include have `RsaPrivateCrtKeyParameters` and not `Org.BouncyCastle.Utilities.IO.Pem` This is why @Subbu has an invalid arguments during compilation. – CodeMonkeyKing Jan 16 '13 at 22:51
  • 1
    @CodeMonkeyKing Thanks. I had the wrong `using` and have been beating my head against this for a while. `..IO.Pem` seems like such a plausible namespace. – Basic Apr 13 '15 at 09:33
5

I found out that the PEMwriter only works for .NET 2.0 in VS2005. The .NET 3.5 SDK environment underlines the pemWriter.WriteObject(keyPair.Private); as an error due to a cast issue. If you try casting this as a PEMObjectGenerator and finally build and debug the code, an InvalidCastException is thrown when the debugger gets to this line of code. I will make this known in the bouncy castle forum as well.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
Subbu
  • 839
  • 2
  • 12
  • 34
2

It appears that the Bouncy Castle C# API has changed and the current answer no longer works. I could not find any answers on SO for how to use it now, so I'll leave this updated example. The trick is to use MiscPemGenerator.

void ConvertPfxToPem(
    string pfxPath,
    string pfxPassword,
    string keyPath)
{
    using (Stream stream = File.Open(pfxPath, FileMode.Open))
    {
        Pkcs12Store pkcs = new Pkcs12Store(stream, pfxPassword.ToCharArray());

        foreach (string alias in pkcs.Aliases)
        {
            if (pkcs.IsKeyEntry(alias) && pkcs.GetKey(alias).Key.IsPrivate)
            {
                AsymmetricKeyParameter privateKey = pkcs.GetKey(alias).Key;

                using (Stream s = new FileStream(keyPath, FileMode.Create))
                using (TextWriter textWriter = new StreamWriter(s))
                {
                    var generator = new MiscPemGenerator(privateKey);

                    PemWriter pemWriter = new PemWriter(textWriter);
                    pemWriter.WriteObject(generator);
                    textWriter.Flush();
                }
            }
        }
    }
}
Alden
  • 2,229
  • 1
  • 15
  • 21
0

System.Security.Cryptography.X509.x509certificate2 class has PrivateKey property

you can get x509certificate2 object from an X509Store object

If you have the rights to the private key then it will be in the X509Certificate2 object (so this object doesnt really represent just a cert)

pm100
  • 48,078
  • 23
  • 82
  • 145