An ASP.NET or COM+ application attempts to import a certificate in a PFX file (PKCS12) programmatically using the X509Certificate or X509Certificate2 classes with code similar to the following:
X509Certificate cert = new X509Certificate("a.pfx", "password");
System.Security.Cryptography.CryptographicException: The system cannot find the file specified
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromFile(String fileName, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromFile(String fileName, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName, String password)
There two causes for this exception:
- The specified PFX file doesn't exist or the name was spelled incorrectly.
- The X509Certificate and X509Certificate2 class constructors which accept a PFX file name and a password require the caller's user account profile to be loaded. However, ASP.NET and COM+ applications run in accounts which usually do not have the user profile loaded.
The X509Certificate2 class constructors attempt to import the certificate into the user profile of the user account that the application runs in. Many times, ASP.NET and COM+ applications impersonate clients. When they do, they do not load the user profiles for the impersonated user for performance reasons. So, they cannot access the "User" certificate store for the impersonated user.
The same code will work when run from an interactive application or a Windows service that is running under a user account because the profile is loaded when the user is logged on or the service started.
To solve this problem, please perform the following steps:
1. An administrator on the machine where the ASP.NET/COM+ application runs should install the certificate in the machine certificate store, called the "Local Computer" store. This should be done when the ASP.NET/COM+ application is installed.
2. The administrator should set the permissions on the private key associated with the certificate to give the ASP.NET process and the impersonated users access to the key. This is needed because only the user account that installs the certificate or private key in the "Local Computer" store can later use the RSA private key associated with the certificate. Use WinHttpCertCfg.exe available from the Windows Resource Kit Tools (http://msdn2.microsoft.com/en-us/library/aa384088.aspx) to configure the permissions.
The command you will use will depend on the user account under which the application code may be running. For example, if the ASP.NET application always runs under a dedicated user account, you will use that account. You can determine the name of the account by calling WindowsIdentity.GetCurrent().Name to find what account you need to grant access. Below are some examples:
a) To grant access to ASPNET account:
winhttpcertcfg -g -c LOCAL_MACHINE\MY -s MyCertificate -a ASPNET
b) To grant access to Network Service:
winhttpcertcfg -g -c LOCAL_MACHINE\MY -s MyCertificate -a "Network Service"
c) To grant access to Authenticated Users:
winhttpcertcfg -g -c LOCAL_MACHINE\MY -s MyCertificate -a "Authenticated Users"
where MyCertificate is the subject name. For example, if the subject name of your certificate contains as an example CN=test name,OU=… you will actually pass the string "test name" for -s command line parameter above.
3. The ASP.NET/COM+ application code should use the installed certificate rather than attempt to install one from a PFX file. Have the code locate the installed certificate using X509Store class.
a. Use StoreLocation.LocalMachine in the constructor of X509Store.
b. Once the store is opened, locate the desired certificate based on subject name programmatically using X509Certificate2Collection.Find method.
Example code will look similar to:
X509Store store = new X509Store("My", StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
System.Security.Cryptography.X509Certificates.X509Certificate2 newCert =
store.Certificates.Find(X509FindType.FindBySubjectName, "XXXXXXXXXXXXX", false);
The subject name to use above in the second parameter of Find method will depend on the subject name of the installed certificate from p12 or PFX file in the Local Computer certificate store.
901183 How to call a Web service by using a client certificate for authentication in an ASP.NET Web application
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND/OR ITS SUPPLIERS DISCLAIM AND EXCLUDE ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO REPRESENTATIONS, WARRANTIES, OR CONDITIONS OF TITLE, NON INFRINGEMENT, SATISFACTORY CONDITION OR QUALITY, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE MATERIALS.
Article ID: 948154 - Last Review: Mar 10, 2008 - Revision: 1