Convert a .PEM certificate to .PFX programmatically using OpenSSL

opensslpempfxpkcs#12x509certificate2

I've got a .PEM file that I want to convert to a PKCS12 file (PFX), and I know I can easily accomplish this using the following openssl command:

Create a PKCS#12 file:

 openssl pkcs12 -export -in file.pem -out file.p12 -name "My Certificate"

Which is great, but I'd like to do this programmatically using OpenSSL calls. Unfortunately, documentation for OpenSSL is less than ideal.

I've looked into doing this using other libraries:

Using .NET: I can create a X509Certificate2 object from a PEM file, but this only grabs the first certificate and ignores any intermediate CA's in the PEM file.

Using Mentalis.org Security Library: I can create a Certificate object from a PEM file, but I see the following in the documentation:

Remarks
This implementation only reads
certificates from PEM files. It does
not read the private key from the
certificate file, if one is present.

So, that doesn't help me. I need that private key too.

I basically need to recreate the OpenSSL command line tool operation for going PEM>PFX, but in code.

Is there an easier way to do this?

Best Answer

You could use BouncyCastle (assuming C#, because you mentioned .NET).

Let's say localhost.pem here contains both the certificate and the private key, something like this should work:

using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.IO;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Security;

namespace TestApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            StreamReader sr = File.OpenText("localhost.pem");
            IPasswordFinder passwordFinder = new PasswordStore("testtest".ToCharArray());
            PemReader pemReader = new PemReader(sr, passwordFinder);


            Pkcs12Store store = new Pkcs12StoreBuilder().Build();
            X509CertificateEntry[] chain = new X509CertificateEntry[1];
            AsymmetricCipherKeyPair privKey = null;

            object o;
            while ((o = pemReader.ReadObject()) != null)
            {
                if (o is X509Certificate)
                {
                    chain[0] = new X509CertificateEntry((X509Certificate)o);
                }
                else if (o is AsymmetricCipherKeyPair)
                {
                    privKey = (AsymmetricCipherKeyPair)o;
                }
            }

            store.SetKeyEntry("test", new AsymmetricKeyEntry(privKey.Private), chain);
            FileStream p12file = File.Create("localhost.p12");
            store.Save(p12file, "testtest".ToCharArray(), new SecureRandom());
            p12file.Close();
        }
    }

    class PasswordStore : IPasswordFinder
    {
        private char[] password;

        public PasswordStore(
                    char[] password)
        {
            this.password = password;
        }

        public char[] GetPassword()
        {
            return (char[])password.Clone();
        }

    }
}

You'll probably need something a bit more subtle for the IPasswordFinder and if you want to handle certificate chains correctly. For more advanced features, you may be able to find more details in the BouncyCastle examples.