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:
X509KeyStorageFlags
value.X509KeyStorageFlags.Exportable
.w3wp.exe
user-profile is not loaded:
X509KeyStorageFlags.UserKeySet
is not set, but otherwise always succeeds.X509KeyStorageFlags.Exportable
, but otherwise always succeeds, otherwise it fails with "Key not valid for use in specified state."WEBSITE_LOAD_CERTIFICATES
defined:
MachineKeySet
set and UserKeySet
is not set fails with a CryptographicException
: "Access denied."keyStorageFlags
value, including values like UserKeySet | MachineKeySet | Exportable
or just DefaultKeySet
fails with a CryptographicException
: "The system cannot find the file specified."WEBSITE_LOAD_CERTIFICATES
defined as the thumbprint of the certificate that was uploaded:
MachineKeySet
and UserKeySet
is not set, fails with CryptographicException
: "Access denied." .
UserKeySet
and UserKeySet | MachineKeySet
and Exportable
will work.X509KeyStorageFlags.Exportable
- same as all other environments.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?
I thought more about how WEBSITE_LOAD_CERTIFICATES
seems to make a difference - but I had a funny feeling about it really only working with the certificate thumbprint that's specified.
So I changed the WEBSITE_LOAD_CERTIFICATES
value to a dummy thumbprint - an arbitrary 40-character Base16 string, and re-ran my test - and it worked, even though the thumbprint had no relation to the certificate I was working with.
It seems that simply having WEBSITE_LOAD_CERTIFICATES
defined will enable the the Azure website's ability to use X509Certificate
and X509Certificate2
- even if the loaded certificate is never installed into, or even retrieved from, any systemwide or user-profile certificate store (as seen in the Certificates snap-in for MMC.exe).
This behaviour does not seem to be documented anywhere, so I'm mentioning it here.
I've contacted Azure support about this.
Regarding the behavioural change I noticed at mid-year - it's very likely that I did have WEBSITE_LOAD_CERTIFICATES
originally set for a testing certificate we were using. When I made a new deployment later in the year around June I must have reset the Application settings which removed the WEBSITE_LOAD_CERTIFICATES
and so broke X509Certificate2
instances.
portal.azure.com
WEBSITE_LOAD_CERTIFICATES
, and provide a dummy (fake, made-up, randomly-generated) value for it.X509Certificate2( Byte[], String, X509KeyStorageFlags )
constructor will now work, but note:
keyStorageFlags: X509KeyStorageFlags.MachineKeySet
will fail with "Access denied"keyStorageFlags
values, including MachineKeySet | UserKeySet
will succeed (i.e. MachineKeySet
by itself will fail, but MachineKeySet
used in conjunction with other bits set will work).If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With