X509Certificate Constructor Exception

//cert is an EF Entity and 
//    cert.CertificatePKCS12 is a byte[] with the certificate.

var certificate = new X509Certificate(cert.CertificatePKCS12, "SomePassword");

When loading a certificate from our database, on our staging server (Windows 2008 R2/IIS7.5) we get this exception:

System.Security.Cryptography.CryptographicException: An internal error occurred.

   at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
   at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
   at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)

NOTE: This issue does not happen locally (Windows 7/Casini).

Any insight is greatly appreciated.


Solution 1:

Turns out there's a setting in the IIS Application Pool configuration (Application Pools > Advanced Settings) to load the user profile for the application pool identity user. When set to false, the key containers aren't accessible.

So just set Load User Profile option as True

App Pool-> Advanced Settings Screen

Solution 2:

More than likely, when you are running from Visual Studio/Cassini, it is accessing your user certificate store, even though you're loading it from bytes. Could you please try this and see if it solves your issue:

var certificate = new X509Certificate(
    cert.CertificatePKCS12, "SomePassword", X509KeyStorageFlags.MachineKeySet);

This will cause IIS (which runs as the ASP.NET user which likely doesn't have access to a user store) to use the Machine store.

This page explains the constructor in more detail, and this page explains the X509KeyStorageFlags enumeration.

Edit: Based on the second link from cyphr, it looks like it might be a good idea (if the previous solution doesn't work), to combine some of the FlagsAttribute enumeration values like so:

var certificate = new X509Certificate(
    cert.CertificatePKCS12, "SomePassword",
    X509KeyStorageFlags.MachineKeySet
    | X509KeyStorageFlags.PersistKeySet
    | X509KeyStorageFlags.Exportable);

Additionally, if you have access, you may want to try changing your Application Pool setting to use LocalService (and then restart the AppPool). This may elevate your permissions to an appropriate level if that is the problem.

Finally, you can use File.WriteAllBytes to write out the CertificatePKCS12 contents to a pfx file and see if you can manually import it using the certificate console under MMC (you can delete after successful import; this is just to test). It could be that your data is getting munged, or the password is incorrect.

Solution 3:

Use this code:

certificate = new X509Certificate2(System.IO.File.ReadAllBytes(p12File)
                                   , p12FilePassword
                                   , X509KeyStorageFlags.MachineKeySet |
                                     X509KeyStorageFlags.PersistKeySet | 
                                     X509KeyStorageFlags.Exportable);

Solution 4:

I had trouble on Windows 2012 Server R2 where my application could not load certificates for a PFX on disk. Things would work fine running my app as admin, and the exception said Access Denied so it had to be a permissions issue. I tried some of the above advice, but I still had the problem. I found that specifying the following flags as the third parameter of the cert constructor did the trick for me:

 X509KeyStorageFlags.UserKeySet | 
 X509KeyStorageFlags.PersistKeySet | 
 X509KeyStorageFlags.Exportable

Solution 5:

To be able really solve your problem and not just guess, what can it be, one need be able to reproduce your problem. If you can't provide test PFX file which have the same problem you have to examine the problem yourself. The first important question is: are the origin of the exception "An internal error occurred" in the private key part of the PKCS12 or in the public part of the certificate itself?

So I would recommend you to try to repeat the same experiment with the same certificate, exported without private key (like .CER file):

var certificate = new X509Certificate(cert.CertificateCER);

or

var certificate = new X509Certificate.CreateFromCertFile("My.cer");

It could help to verify whether the origin of your problem is the private key or some properties of the certificate.

If you will have problem with the CER file you can safe post the link to the file because it have public information only. Alternatively you can at least execute

CertUtil.exe -dump -v "My.cer"

or

CertUtil.exe -dump -v -privatekey -p SomePassword "My.pfx"

(you can use some other options too) and post some parts of the output (for example properties of the private key without the PRIVATEKEYBLOB itself).