Had a problem where my app would work fine locally, but fail when hosted on azure app services.
I was trying to load a certificate from a file using new X509Certificate2(string fileName, string password)
. (Although hrom cursory research the error message is identically as ambiguous with other construction methods)
The error message I received was:
Unhandled Exception: Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The system cannot find the file specified
at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, String password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName)
at ConsoleApplication.Program.Main(String[] args)
The certificate was, indeed, present and was readable from the user running - so it's not an issue that the certificate can't be found!
The actual error is, I believe, that it can't put the certificate the LocalUser's cert store. Info here.
I think the culprit is here
https://github.com/dotnet/corefx/blob/29cd6a0b0ac2993cee23ebaf36ca3d4bce6dd75f/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Windows/CertificatePal.Import.cs#L150-L152
I believe the last hresult from PFXImportCertStore will be CRYPT_E_NOT_FOUND "can't find file". Which causes ambiguity between "can't find the given certificate file to import" and "got an error when trying to put the certificate after trying to put it in a store".
Not sure how you'd go about solving this as we're only returned CRYPT_E_NOT_FOUND for failed imports regardless of reason.
Repro:
new X509Certificate2(string fileName, string password)
Workaround:
Store it in the MachineKey set
new X509Certificate2(filePath, pass, X509KeyStorageFlags.MachineKeySet);
https://github.com/dotnet/corefx/issues/11042#issuecomment-250818055
I think this is sufficiently disruptive?
There's nothing we could do here to identify which reason produced that error, so intercepting this with a custom message wouldn't do any good.
The problem isn't that it couldn't add something to the users _cert_ store, it's that it couldn't save the private key (probably because the user had no profile, so no place to save it). It's the _key_ store, not the cert store, at "fault" here.
Instead of MachineKeySet you could also try EphemeralKeySet, which means "don't write the private key down, just keep it in memory".
Since "there's nowhere to save the private key" and "I don't know what file you mean" look the same to us, and might also be used for other errors, I'm still not comfortable replacing the exception message. So there's nothing here we can change.
Ahhhh! Amazing, thanks for clearing that up. Good to know exactly what the
error is.
On 23 Feb 2018 2:37 am, "Jeremy Barton" notifications@github.com wrote:
Closed dotnet/corefx#27358 https://github.com/dotnet/corefx/issues/27358.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/dotnet/corefx/issues/27358#event-1488314809, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAOoMVov6il8iO4xF_4xbGsCQGLDrz2eks5tXiR-gaJpZM4SPjgr
.
Just tried EphemeralKeySet and that doens't work on App Services.
I'm not entirely convinced this can't be improved upon -- but I suppose I'll just have to trust you..
I too think there must be a way to inform the caller about the missing certificate store instead of the generic "file not found" error.
The lines
var pfxBytes = File.ReadAllBytes(pfxFileName);
var certificateCollection = new X509Certificate2Collection();
certificateCollection.Import(
pfxBytes,
password,
X509KeyStorageFlags.UserKeySet);
in Azure App Service for a .NET Core 2.0 ASP.NET app fail with that error.
How should the developer know that the problem is that the user certificate store doesn't exist? - In this case, the PFX file has been successfully loaded, so for the developer, there is no file load operation.
By the way, just changing to X509KeyStorageFlags.MachineKeySet
or X509KeyStorageFlags.EphemeralKeySet
did not work for me. I had to 'or' them:
certificateCollection.Import(
pfxBytes,
password,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet);
I had the same problem and was unable to solve it by changing any combination of storage flags.
My workaround:
You can access a private cert by adding its thumbprint to your application settings under "WEBSITE_LOAD_CERTIFICATES" which will put it in the local users' store.
https://docs.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load
(for the benefit of anyone else that hits this problem)
I hit this error attempting to import certificate bytes from an app config setting (injected from a key vault secret at runtime) with new X509Certificate2(Convert.FromBase64String(config.CertificateBase64Bytes))
My solution was found at https://docs.microsoft.com/en-us/azure/app-service/app-service-web-ssl-cert-load#load-certificate-from-file
It seems that even though I'm not explicitly interacting with a certificate store, it still errors because there is no user profile loaded. The solution for me was to set the app config setting WEBSITE_LOAD_USER_PROFILE = 1
If your running on a windows server you need to enable load user profile on the APP Pool identity to get a user key store. The x509Certifcate2 constructors seem to need a key store maybe?
Most helpful comment
I too think there must be a way to inform the caller about the missing certificate store instead of the generic "file not found" error.
The lines
in Azure App Service for a .NET Core 2.0 ASP.NET app fail with that error.
How should the developer know that the problem is that the user certificate store doesn't exist? - In this case, the PFX file has been successfully loaded, so for the developer, there is no file load operation.
By the way, just changing to
X509KeyStorageFlags.MachineKeySet
orX509KeyStorageFlags.EphemeralKeySet
did not work for me. I had to 'or' them: