I have a .NET Framework 4.7 application that allows users to upload X.509 certificates in PFX or PKCS#12 format (think: "SSL certificates" with the private key included), it then loads the certificate into a System.Security.Cryptography.X509Certificates.X509Certificate2
instance. As my application code also needs to re-export the certificate I specify the X509KeyStorageFlags.Exportable
option.
When running under IIS on my production web-server, the Windows user-profile for the identity that w3wp.exe
runs under is not loaded, so I do not specify the UserKeySet
flag.
String filePassword = ...
Byte[] userProvidedCertificateFile = ...
using( X509Certificate2 cert = new X509Certificate2( rawData: userProvidedCertificateFile, password: filePassword, keyStorageFlags: X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet )
{
...
}
In early 2017 I deployed this code to an Azure App Service (aka Azure Website) instance and it worked okay - after initially failing because I did have the UserKeySet
flag set (as Azure App Services do not load a user-profile certificate store.
However, since mid-2017 (possibly around May or June) my application has stopped working - I assume the Azure App Service was moved to an updated system (though Kudu reports my application is running on Windows Server 2012 (NT 6.2.9200.0
).
It currently fails with two error messages that varied depending on input:
CryptographicException
"The system cannot find the file specified."CryptographicException
"Access denied."
I wrote an extensive test-case that tries different combinations of X509Certificate2
constructor arguments, as well as with and without the WEBSITE_LOAD_CERTIFICATES
Azure application setting.
Here are my findings when working with an uploaded PFX/PKCS#12 certificate file that contains a private key and does not have password-protection:
- Running under IIS Express on my development box:
- Loading the certificate file always succeeds, regardless of
X509KeyStorageFlags
value. - Exporting the certificate file requires at least
X509KeyStorageFlags.Exportable
.
- Loading the certificate file always succeeds, regardless of
- Running under IIS on a production server (not an Azure App Service) where the
w3wp.exe
user-profile is not loaded:- Loading the certificate file requires that
X509KeyStorageFlags.UserKeySet
is not set, but otherwise always succeeds. - Exporting the certificate file requires at least
X509KeyStorageFlags.Exportable
, but otherwise always succeeds, otherwise it fails with "Key not valid for use in specified state."
- Loading the certificate file requires that
- Running under Azure App Service, without
WEBSITE_LOAD_CERTIFICATES
defined:- Loading the certificate with
MachineKeySet
set andUserKeySet
is not set fails with aCryptographicException
: "Access denied." - Loading the certificate with any other
keyStorageFlags
value, including values likeUserKeySet | MachineKeySet | Exportable
or justDefaultKeySet
fails with aCryptographicException
: "The system cannot find the file specified." - As I was not able to load the certificate at all I could not test exporting certificates.
- Loading the certificate with
- Running under Azure App Service, with
WEBSITE_LOAD_CERTIFICATES
defined as the thumbprint of the certificate that was uploaded:- Loading the certificate with
MachineKeySet
andUserKeySet
is not set, fails withCryptographicException
: "Access denied." .- So values like
UserKeySet
andUserKeySet | MachineKeySet
andExportable
will work.
- So values like
- Exporting certificates requires
X509KeyStorageFlags.Exportable
- same as all other environments.
- Loading the certificate with
So it seems that WEBSITE_LOAD_CERTIFICATES
seems to work - but only if the certificate being loaded into an X509Certificate2
instance has the same thumbprint as specified in WEBSITE_LOAD_CERTIFICATES
.
Is there any way around this?