Search code examples
c#asp.netasp.net-corecryptographyidentityserver4

Identity Server 4 WindowsCryptographicException: Keyset does not exist


I get these these two exceptions in Identity Server 4 during a regular login to get a token. It's on a load balanced setup with 2 servers, so I've done the following: They both get the same PFX for encrypting tokens. It works just fine on a single server. They have the password for the PFX file, and can seemingly encrypt a token correct correctly.

Sometimes the flow works well, but it always fails on giving CORS access on the connect/token endpoint. I can see that the CORS Middleware plays a role here, but I don't understand why the signing are credentials during the verification of CORS access. I've added protection here, and during startup, I verify that the private key for both the protectionCert and the signing credentials exist. Both certs are password-protected files included with the deploy just to keep it simple(i.e. not running into access issues on private key).

Why is it all of a sudden having trouble getting access to the private key during the CORS operation though? That part I do not understand. All of this works on my test environment, which runs on a single machine, so I'm 99% sure it has to do with this setup being load balanced. Also wondering, if I can save myself a headache by enabling sticky sessions in the load balancer.

In my DataProtection store there's only a single key saved. Not sure if that's normal. I would expect the application to generate more keys.

                    services.AddDataProtection()
                    .SetApplicationName("MyApp")
                    .PersistKeysToDbContext<DataProtectionKeyContext>()
                    .ProtectKeysWithCertificate(protectionCert);


                    var cert = new X509Certificate2("signing.pfx",
                    configuration.GetValue<string>("AppSettings:SigningCredentials:FilePassword"));
                    builder.AddSigningCredential(cert);
Unhandled exception: "Keyset does not exist" ";"Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist
   at System.Security.Cryptography.CngKey.Open(String keyName, CngProvider provider, CngKeyOpenOptions openOptions)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PrivateKey()
   at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PrivateKeyStatus()
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
   at IdentityServer4.Services.DefaultTokenCreationService.CreateJwtAsync(JwtSecurityToken jwt)
   at IdentityServer4.Services.DefaultTokenCreationService.CreateTokenAsync(Token token)
   at IdentityServer4.Services.DefaultTokenService.CreateSecurityTokenAsync(Token token)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.CreateAccessTokenAsync(ValidatedTokenRequest request)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAsync(TokenRequestValidationResult request)
   at IdentityServer4.Endpoints.TokenEndpoint.ProcessTokenRequestAsync(HttpContext context)
   at IdentityServer4.Endpoints.TokenEndpoint.ProcessAsync(HttpContext context)
   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
   
   
   Connection ID ""17365880169046365174"", Request ID ""800253f9-0001-f100-b63f-84710c7967bb"": An unhandled exception was thrown by the  application.";
   "Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist
   at System.Security.Cryptography.CngKey.Open(String keyName, CngProvider provider, CngKeyOpenOptions openOptions)
   at Internal.Cryptography.Pal.CertificatePal.GetPrivateKey[T](Func`2 createCsp, Func`2 createCng)
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PrivateKey()
   at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PrivateKeyStatus()
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
   at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)
   at IdentityServer4.Services.DefaultTokenCreationService.CreateJwtAsync(JwtSecurityToken jwt)
   at IdentityServer4.Services.DefaultTokenCreationService.CreateTokenAsync(Token token)
   at IdentityServer4.Services.DefaultTokenService.CreateSecurityTokenAsync(Token token)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.CreateAccessTokenAsync(ValidatedTokenRequest request)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request)
   at IdentityServer4.ResponseHandling.TokenResponseGenerator.ProcessAsync(TokenRequestValidationResult request)
   at IdentityServer4.Endpoints.TokenEndpoint.ProcessTokenRequestAsync(HttpContext context)
   at IdentityServer4.Endpoints.TokenEndpoint.ProcessAsync(HttpContext context)
   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
   at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events)
   at IdentityServer4.Hosting.MutualTlsEndpointMiddleware.Invoke(HttpContext context, IAuthenticationSchemeProvider schemes)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.<Invoke>g__InvokeCoreAwaited|15_0(HttpContext context, Task`1 policyTask)
   at IdentityServer4.Hosting.BaseUrlMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at NewRelic.Providers.Wrapper.AspNetCore.WrapPipelineMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

Solution

  • I think I solved it. I read that it could have to do with lack of access. I guess on of my tasks in my CD setup isn't working right, because I tried running an APP CMD command to set user profile to loaded as true for my app pool. Supposedly, if this flag is false, the application will by default try to store the private key as the current user, but since no user is loaded, this doesn't work. So I tried with ephemeral keyset instead(in-memory) to avoid access issues and now it works. Not sure what the consequences are of using an in-memory keyset, if the keys are appropriate etc.