I am developping a Hosted Blazor WebAssembly application with Identity Server. It works perfectly in my development environment, but fails in a production environment.
To try and find the reason, I went back to the templates supplied with Visual Studio and got the same issue. So here are the details to replicate.
The application runs fine locally.
Before publishing it:
IdentityServer
section in appsettings.json:
"IdentityServer": {
"key": {
"Type": "File",
"FilePath": "certificate.pfx",
"Password": "password"
},
"Clients": {
"BlazorWasmIdentityHosted.Client": {
"Profile": "IdentityServerSPA"
}
}
}
<aspNetCore ...>
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
I then get an error page as soon as I request the home page
CryptographicException: File not found.
System.Security.Cryptography.X509Certificates.CertificatePal.FilterPFXStore(ReadOnlySpan<byte> rawData, SafePasswordHandle password, PfxCertStoreFlags pfxCertStoreFlags)
InvalidOperationException: There was an error loading the certificate. Either the password is incorrect or the process does not have permisions to store the key in the Keyset 'DefaultKeySet'
Microsoft.AspNetCore.ApiAuthorization.IdentityServer.SigningKeysLoader.LoadFromFile(string path, string password, X509KeyStorageFlags keyStorageFlags)
As I am deploying to a hosting provider where I don't have a direct access to IIS, I suspect that I don't have the rights to add an item to a certificate store.
I tried deploying to IIS on my local machine, it runs fine.
So, my question is: Is there any other way to manage a signing key in this specific configuration: IIS on a shared hosting provider (managed with Plesk)?
The Microsoft documentation gives details about deploying to Azure (here), but I didn't find much on deploying to IIS in my situation.
Edited:
I finally think the issue was with lack of access rights for the IIS user on the hosting provider. Not sure if that can be fixed (depends on the provider), so I finally moved to Azure. Setting and deploying need some care, but at least it's well documented by Microsoft, and now it works for me.
I recently came across an article discussing common issues when dealing with X509Certificate2 object creation, handling private key material in application code. One of the common problems is certificate and private key handling inside .NET applications. Most of the problems for these use cases occur in web applications when certificate lookup/instantiation fails. For example, certificate reading works on a developer machine, but fails when the application is deployed to production. I want to share the content of this article with you, in order to avoid the article link from expiring, I quoted the content from the article in the answer, hoping to bring you some help.
There are two common deployment models for application certificates:
Persistent - certificate is installed in certificate store which is independent to application (such as Windows Certificate Store). The installed certificate is shared with any other application that runs under same security account.
In-app - The certificate is either embedded in the app itself or stored as an external protected file. This includes PFX files, keychains, Azure Key Vault, and many others.
Pros and Cons of the Persistent Deployment Model:
The benefit of the persistent deployment model is simplified certificate management, which can be easily installed and managed using external mechanisms such as certificate autoenrollment and Certificate Lifecycle Manager (CLM). CLM makes it easy to manage certificates in the certificate store without having to update or redeploy applications every time certificates are renewed.
Of course the downside of this mode is that there is no certificate isolation/protection between processes. When the application is deleted, the certificate is not deleted and remains on the system. Even if the certificate is deleted from persistent storage (Windows certificate store), the private key is not necessarily deleted: case of accidental deletion of user certificates. Another process under the same security account or a computer administrator can easily access the certificate and its private key material.
The in-app deployment model has the opposite characteristics of the persistent model.
The benefit of the in-app model is better certificate and private key security. Private keys may not be shared with other applications running under the same security account, and computer administrators may be prevented from accessing private key material. The application loads the certificate into memory only when necessary, and then unloads the certificate and private key.
There is a parameter called password which is known only to authorized applications and which provides private key isolation between processes. Applications that do not know the password can’t access the certificate and private key. Although the code looks secure, in fact it is not in the current way. The problem is with default behavior of the X509Certificate2 constructors that import certificate from PFX. Using default constructors, such as X509Certificate2(String, String), X509Certificate2(String, SecureString), X509Certificate2(Byte[], String), X509Certificate2(Byte[], SecureString), private key is silently copied and persisted on a system, this will introduce security holes in working code. Any simple application/script can use FileSystemWatcher to detect key file creation and immediately create a copy of the key file.
To avoid this bad behavior, a constructor with three parameters must be used. The third parameter is the X509KeyStorageFlags enumeration type.
DefaultKeySet — copies private key to default store. It can be specified in PFX properties (user key set or machine key set). If none specified in PFX, user key set is used. This is something you don’t want to use, because it persists the key on a disc. And if PFX specifies Machine Key Set and application doesn’t have local administrator/system permissions, the call will fail with exception.
UserKeySet — forces private key copy to user profile regardless what is specified in PFX properties.
MachineKeySet — forces private key copy to local system profile. If application doesn’t have local administrator/system permissions, the call will immediately fail.
Exportable — copies private key to default key set (see rules above) and makes private key exportable. This makes zero sense, because application doesn’t need to have an access to private key material. If the application require this, the application is simply wrong. Instead, the application must deal with handles provided by operating system. Nothing else.
UserProtected — this flag enables private key strong protection. This doesn’t work with any non-interactive applications, because raise UI popups and require user input. And in web/background applications the popup is raised in background sessions you cannot access.
PersistKeySet — is similar to DefaultKeySet with the exception that private key permanently persists on a disk even if you explicitly release all handles.
EphemeralKeySet — the only flag that doesn’t copy private key material on a disk and the only that makes sense, because it does exactly what we need — temporarily load the key from PFX and unload when finished.
When loading a certificate with a private key from PFX or Azure Key Vault, you >should use the EphemeralKeySet enumeration value.
For more detailed content, you can get an in-depth understanding through the article link provided above.