24

I am trying to programmatically import a X509 certificate (pfx / PKCS#12) in my local machine's certificate store. This particular certificate has a chain of certificates, the certification path looks something like this:

  • Root certificate CA
    • Organization certificate CA
      • Organization 2 certificate CA
        • My certificate

The code I use looks like this:

cert = new X509Certificate2(pathToCert, password);

if (cert != null)
{
    var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadWrite);
    if (!store.Certificates.Contains(cert))
    {
        store.Add(cert);
    }
}

This code does import the certificate, however it seems to ignore the chain. If I check the certificate in the store, the certification path only shows:

  • My certificate

However when I import the pfx manually, it does show the full path. Am I skipping a step here, or am I missing some parameter? Can someone shed some light on this?

Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
  • A PFX is usually confused with PKCS#12, In your case you just want a certificate chain, which you can easily do with PEM files. But.. Do you have your own private key file? in which case you do want PKCS#12 – IanNorton Feb 04 '12 at 19:19
  • @IanNorton: It actually is a PKCS#12 certificate. It is not like I have any choice in that. – Edwin de Koning Feb 05 '12 at 10:35
  • In which case, you want to read [this about pkcs#12](http://stackoverflow.com/questions/555184/decode-a-pkcs12-file) You have a p12 file that contains x509 objects. – IanNorton Feb 05 '12 at 19:03
  • Is there any way to use that store as the store to validate certificates on a SOAP call?? – João Antunes Apr 16 '18 at 21:47

4 Answers4

23

You should be able to iterate over the certs in your PFX (and import each into the cert store of your choice) by opening the PFX file as an X509Certificate2Collection object.

Here are the docs on X509Certificate2Collection:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2collection.aspx

MSDN provides some sample code in that docs page on how to inspect each cert in the collection.

Once you know the CNs/Issuers/other info about each cert it should be clear which certificate store each one needs to be added to. For that you can use the X509Store class and the StoreName enumeration to specify which store you want to open/add to:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509store.aspx

http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.storename.aspx

Also see my answer to a similar SO question:

How to retrieve certificates from a pfx file with c#?

As mentioned in one of the latest comments on that answer, when you try to import a cert to the current user's Root store ("StoreName.Root" and "StoreLocation.CurrentUser" as the name/location) you will get a popup dialog asking you to confirm.

To solve that I just added a little MS UI Automation code to my cert import method, to click OK on the prompt.

Or, as the commenter "CodeWarrior" says in the other SO answer's comment, to avoid the popup dialog you can try putting the root cert into the LocalMachine store instead of CurrentUser.

Sample code:

string certPath = <YOUR PFX FILE PATH>;
string certPass = <YOUR PASSWORD>;

// Create a collection object and populate it using the PFX file
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet);

foreach (X509Certificate2 cert in collection)
{
    Console.WriteLine("Subject is: '{0}'", cert.Subject);
    Console.WriteLine("Issuer is:  '{0}'", cert.Issuer);

    // Import the certificate into an X509Store object
}
Community
  • 1
  • 1
Bill Agee
  • 3,606
  • 20
  • 17
  • 1
    This does not work in my case, after importing the collection contains just one certificate (My Certificate). I think you're on to something though. – Edwin de Koning Feb 06 '12 at 08:54
  • 1
    I had the same problem at one time (where opening the PFX file as an X509Certificate2 object resulted in only the leaf/user cert being imported). So X509Certificate2 apparently doesn't work unless you only need the leaf cert. Opening the PFX as an X509Certificate2Collection was the fix - when you do that and check the collection's length (or print the Subject/Issuer of each cert in the collection in a for loop) do you see all the certs in the chain? – Bill Agee Feb 06 '12 at 16:15
  • Finally I seem to be getting somewhere. I did a fresh export from my cert store and now it seems to be working with your code. It shows all certifactes now, EXCEPT the root certificate... Any ideas? – Edwin de Koning Feb 09 '12 at 14:03
  • To get a second opinion on the contents of the PFX file, it would be interesting to try using "openssl.exe pkcs12" (http://www.slproweb.com/products/Win32OpenSSL.html) to list the cert chain as shown here: http://www.planetlarg.net/support-cookbook/ssl-secure-sockets-layer/decode-pfx-file-using-openssl – Bill Agee Feb 09 '12 at 19:44
  • Turns out your solution showed the correct results after all. Thanks again and congratulations on the bounty. I also found another way of doing this, using the X509Chain object, which I will post as an answer below. – Edwin de Koning Feb 13 '12 at 12:36
  • Glad it worked out, and thanks for the information on X509Chain, looks very useful! – Bill Agee Feb 13 '12 at 18:59
  • 2
    FYI for anyone using this in Powershell without the benefit of compilation errors, X509Certificate2Collection.Import does NOT have a SecureString overload like the X509Certificate2.Import method does. – Simon Francesco Mar 25 '14 at 01:50
10

For future reference, I discovered another way of doing this, using the X509Chain object:

var cert = new X509Certificate2(pathToCert, password);

X509Chain chain = new X509Chain();
chain.Build(cert);
for (int i = 0; i < chain.ChainElements.Count; i++)
{
   //add to the appropriate store
}
Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
1

For anyone who wants "to the appropriate store" code generic solution

This is what I've created using VB so shouldn't be to hard to port to C#. I used the above posts to get me started and I'm a total NooB at this.

    Dim certPath = "C:\Users\08353153\Documents\Visual Studio 2015\Projects\WindowsApplication2\WindowsApplication2\bin\Debug\8870-thebigchess.pfx"
    Dim certPass = "eduSTAR.NET"
    Dim Collection As New X509Certificate2Collection
    Collection.Import(certPath, certPass, X509KeyStorageFlags.PersistKeySet)

    Dim certOne As X509Certificate2 = Collection(0)
    Dim certTwo As X509Certificate2 = Collection(2)
    Dim certThree As X509Certificate2 = Collection(1)

    Dim personal As New X509Store(StoreName.My, StoreLocation.LocalMachine)
    personal.Open(OpenFlags.ReadWrite)
    personal.Add(certOne)
    personal.Close()

    Dim trust As New X509Store(StoreName.Root, StoreLocation.LocalMachine)
    trust.Open(OpenFlags.ReadWrite)
    trust.Add(certTwo)
    trust.Close()

    Dim intermed As New X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine)
    intermed.Open(OpenFlags.ReadWrite)
    intermed.Add(certThree)
    intermed.Close()
Clarkey
  • 11
  • 2
-1

An X.509 certificate contains only a chain that links it to the root certificate (including the intermediate authorities), but these certificates are not contained in the certificate. This chain is used when an end certificate (that is not self-signed) is validated - it must lead to a root certificate that is trusted. More precisely the public key of each CA is used to decode and verify the hash for an issued certificate. This process is repeated until the root certificate is reached. After the whole chain is checked, if the root certificate is trusted the end certificate is also trusted. Of course the process includes other validations too (like start date, end date, certificate revocation list for example), but I have detailed only the part related to the usage of the chain.

So you have correctly imported "My certificate" together with the chain to "Root certificate CA" - this chain is encoded in "My certificate" and you can confirm this by viewing its properties, but this chain is only a link and it does not contain any of "Root certificate CA", "Organization certificate CA" and "Organization 2 certificate CA" certificates.

I hope this solves your issue, but if it doesn't, could you be more specific about what are you trying to accomplish?

andrei m
  • 1,157
  • 10
  • 15
  • Thanks for the theory on certificates. Thing is, I am losing the chain information during the import and I am trying to solve this in my code. – Edwin de Koning Feb 05 '12 at 10:37