IdentityServer4 not working in production
Beside the accepted answer, In case if someone wants to create their own SigningCredentials
using the AsymmetricSecurityKey
instead of the 'Key' configuration in appsettings.json, the following can also solve the problem.
- First define the RSA params in the xml file or store in the Application Resource file: e.g (working sample),
<RSAKeyValue>
<Modulus>9+hZCiZrVpqe1t+Q4HOfzrmmkNkNPurX3btOva9Hdx1lIKV7ndeVGCq71plXhW78krXdcDSSeOEVf8W51/Qq0ai8Rg9P1XIcedVgIj7MiHZ+k/rcnq1Y9yg6d1DHXtlAJLasvhCc3o+9inXh2DAzllIxyy4FabS51dRyWDBMA2LS8kS3o5UdcVQYoY+B/9d8qVHmlbQNuao3NL/UqVo6UKnGidSjTQMyPkPJEmpul9C3Cn8Tm7zqKidk2A/obU5bmBFfivhPGoFnahYMs635LpitEPdJGQCfzcmLyE9y23cPyPzowkB/zsONCgWoxglst95vKRWgyXgBXAiSbKtaFQ==</Modulus>
<Exponent>AQAB</Exponent>
<P>/dJVFnxsOJsSZpeK698QH7x/j9UeLlYrWuRsC6BTyPs/YE+aWiZpRkjz6uZcgkCeNVwxU8IdGukyrt87dcgrwuE/QM02ZqINZ65rGXR2/GA9WwmMD+Sf89q10emNURtquP2hF7mE8j224rsNv7+c7VPJmQ5VaXbDyeTYdea4pm8=</P>
<Q>+gkFmWWeWgh1LWJVzzxX0nr8ebQWwut/ca9dnyQf2QeSmVnc+BpFSTJoaUp4TmCkVrDOxVszvUHA7jOQrUHxmHUJ5QCxM/hVN34iVKj5Ic3vZJAMeqc2bsmeFwlKRrXMNSHiju5bQnWymO72Y7T9Ldkqvjke43adto25eEH+Kbs=</Q>
<DP>y1N5a9jiDHpUxDAzTf3TecjTWtH7Kl1Gv7npv2qAk6iIvUsnN347qNz54DsG8iR3WAFxVkpSbGNQgXs7s39VZvhvZia9pHu+R0cWbj64rjUeEVZVh8m6RGr4aZ4w4T8YP/aU3F9122OKpJf5TJhfSlJrVRuBWkmUT5/tsozPcCs=</DP>
<DQ>PcSIy5JdAiTgvatzQ1TG5UpYoMAqd1CyFSWbXTsRWw4R2yxl+CyVPTXksU4iVkptjrTy/7I+H9zkinPWo9aMlnsjTJ1VKV+JvcG9PWjY0s8K+q7TRmGUgt3v3gT/gmRa5C1QyLp9dPeafUlbONp3SSJC+ucliE+/Ol/cl6bF4Q0=</DQ>
<InverseQ>zNXj2pFcx1w+pZqzGCbAwlWDUDd2BtK9t5dTYMTHMCXUCizb0Jiai9cdH6kstoqM4TkL8KTEl3sA6RCm5qpgBPMAT18F6VPzXFUErDtkOGwIuifid3L+CBQ1fjoBZqdC8LfAen3LT5wVn0p6lMouWoAX/NRJnjQGsO/LOHQVJjk=</InverseQ>
<D>1CJujtDxaNpGsXf9cRN/3FXgwnH5c61hqsttRcOHU9ZDgvwYG6kuW8+1jJ0K15NxbdlR84IJFIcG7p8zuCenvGC3Ovw/RFaxJ0//Q06ZluxOxDIWN3H+fwBdh1wIPpGI4eGvT7THh2tYtfLJn0Uf37HWChcHarzLiL6SWgR6ByoKUEFTAbqbrB8mJJbaev83aNGopdjfc4+MntAGaWkpSvMuzY8Mm5CQ9lc5HQ/6a9Cb2O+fSi8P92FHwPWNIqP7pE7BmZeB3WB52K3fhpDHpkS0n0DjrchHLgMLo788IDlglLS+a/OaUX1xAMZMXrLzA/vtE5YJPHvFNAHh7oSCwQ==</D>
</RSAKeyValue>
- Then, we can read the params using the RSA helper method to create the
AsymmetricSecurityKey
. I have created the following helper class for my project:
using System.Security.Cryptography;
public static class MyKeyStore
{
public static AsymmetricSecurityKey Key;
static MyKeyStore()
{
var props = RSA.Create();
props.FromXmlString(Resources.RsaProps);
Key = new RsaSecurityKey(props);
}
}
- Modify the Startup.cs class and use the above helper class to generate the
SigningCredentials
. e.g.,
// configure IDS
services.AddIdentityServer(options =>
{
// ...
}).AddApiAuthorization<ApplicationUser, ApplicationDbContext>
(options => { options.SigningCredential = new SigningCredentials(MyKeyStore.Key, SecurityAlgorithms.RsaSha512);});
Note that if you are wondering about a working example, i have recently used it in a demo project which is up and running and Azure at the moment. Here is the startup file and here is the complete project.
So I was able to solve my issues using this piece of documentation: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0#example-deploy-to-azure-websites
I had to enable "Copy if newer" to the appsettings.json properties so that it would get copied to the build folder.
I also added the following to the appsettings.json file:
"IdentityServer": {
"Clients": {
"Client": {
"Profile": "IdentityServerSPA"
}
},
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "LocalMachine",
"Name": "CN=SigningCertificate"
}
}
Now the Key.Type is specified, which means that we can now just add the following to the startup.cs:
// Configure IdentityServer4
var identityBuilder = services.AddIdentityServer();
identityBuilder.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
if (!Environment.IsDevelopment())
identityBuilder.AddSigningCredentials();
I still do not understand why other people are not experiencing this issue, since I am not able to find any other threads on this issue and the regular way seems to work for everyone else. The only downside to this is that I need to install the certificate on the machine now instead of getting it as file.
Here is how I solved it in Docker for Blazor WebAssembly. My answer is mostly based on this thread. Keep in mind that, although it works, it may not be production-ready, nor safe. I know little about IdentityServer.
appsettings.json:
"IdentityServer": {
//[...]
"Key": {
"Type": "File",
"FilePath": "/path_to_certificate_here/server.pfx",
"Password": "password_specified_later"
}
}
FilePath
is where you physically placed your certificate (generated in next step of this answer). Password
is being configured while generating certificate.
Generating certificate:
Source. This might not be production-ready either.
$ openssl genrsa 2048 > server_private.pem
$ openssl req -x509 -days 1000 -new -key server_private.pem -out server_public.pem
$ openssl pkcs12 -export -in server_public.pem -inkey server_private.pem -out server.pfx
Keep in mind that certificate will expire (-days
attribute in 2nd command).
Working with Docker
There are some answers advising to include certificate in build
folder or keep it with project source code. I personally don't think it's a good idea. I'm generating certificates manually on my server in separated folder, then I'm creating Docker volume pointing to folder where I placed them.
If you want to use *.pfx
"Key": {
"Type": "File",
"FilePath": "certificate.pfx",
"Password": "password:!"
}
And read this thread if you have this error WindowsCryptographicException: Keyset does not exist
internal.cryptography.cryptothrowhelper+windowscryptographicexception keyset does not exist