Why does X509Certificate2 sometimes fail to create from a blob?

jlew picture jlew · Feb 17, 2009 · Viewed 7.5k times · Source

I have an ASP.NET web service which is receiving a byte array representing the contents of a .pfx file containing an X.509 certificate. The server-side code is using the System.Security.Cryptography.X509Certificate2 constructor to load the certificate from the bytes:

X509Certificate2 native_cert = new X509Certificate2(
                pkcs12_buf /*byte array*/,
                password,
                X509KeyStorageFlags.PersistKeySet |
                X509KeyStorageFlags.Exportable
            );

Depending on who my service process is running as, this call will either succeed, or fail with an "internal error" exception. The last call on the exception stack is to X509Utils._LoadCertFromBlob, which is unmanaged code in mscore.dll.

This code succeeds when run from a console application in an interactive login using the service account's credentials. It fails when running under w3wp.exe in an application pool that uses the service account's credentials. Changing the app pool identity to an administrator fixes the problem, so it must be a privilege issue, but I have no idea what privilege could be necessary for this. The code does not touch either the filesystem or the Windows certificate stores.

[UPDATE: More Info]
This error appears in the Windows Event Log:

*Cryptographic Parameters:*   
**Provider Name:**  Microsoft Software Key Storage Provider  
**Algorithm Name:** Not Available.  
**Key Name:**   {E182E13B-166D-472A-A24A-CBEF0808E9ED}  
    **Key Type:**   User key.

*Cryptographic Operation:*  
**Operation:**  Open Key.  
    **Return Code:**    0x2  

Any ideas?

Answer

Sam picture Sam · Feb 27, 2009

I just found the solution to this one myself:

X509KeyStorageFlags flags = X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet;

X509Certificate2 cert = new X509Certificate2(pkcs12_buf, password, flags);

The trick here is to use the local key store MachineKeySet flag instead of the user profile key store, which is the default if you don't specify an alternative location. Because the ASP.NET process identity doesn't load the user profile store, you can't access the store when importing a certificate programmatically, but you can access the machine store.

I think PersistKeySet just keeps the private key loaded, but I'm not sure exactly what it does - it's required if you need to access the private key for some reason though.