views:

1052

answers:

2

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 mscoree.dll.

This code succeeds when run from a console application in an interactive login using the service account's credentials. It fails when running uner 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?

Thanks, Jeremy

A: 

Try granting the ASP.NET account permissions to the following folder: C:\Documents And Settings\All Users\Microsoft\Crypto\RSA\MachineKeys\ (may vary according to your environment)

Darin Dimitrov
I think you're on the right track, but it doesn't seem to be trying to access the filesystem. The account that succeeds accesses Microsoft\Crypto\RSA under the user's personal profile (not AllUsers). The one that fails is not even trying to access the filesystem, according to procmon.
jlew
+1  A: 

Heh, 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.

Sam