IDX10503: Signature validation failed

15,404

If you're generating a new RSA key each time you (re)start your server, then it's not surprising: tokens signed with KEY A cannot be validated using KEY B. For your scenario to work, you need to store your RSA key somewhere and use the same one during startup.

One way is to call rsa.ExportParameters(true) and store the different parameters somewhere, so you can easily retrieve and import them using rsa.ImportParameters(...).


But your best option is to use AspNet.Security.OpenIdConnect.Server, that will automatically generate and store a RSA key for you in the last version:

Startup.cs

    public class Startup {
        public void ConfigureServices(IServiceCollection services) {
            services.AddAuthentication();
            services.AddCaching();
        }

        public void Configure(IApplicationBuilder app) {
            // Add a new middleware validating access tokens issued by the OIDC server.
            app.UseJwtBearerAuthentication(options => {
                options.AutomaticAuthentication = true;
                options.Authority = "resource_server_1";
                options.RequireHttpsMetadata = false;
            });

            // Add a new middleware issuing tokens.
            app.UseOpenIdConnectServer(options => {
                options.AllowInsecureHttp = true;

                options.Provider = new OpenIdConnectServerProvider {
                    // Override OnValidateClientAuthentication to skip client authentication.
                    OnValidateClientAuthentication = context => {
                        // Call Skipped() since JS applications cannot keep their credentials secret.
                        context.Skipped();

                        return Task.FromResult<object>(null);
                    },

                    // Override OnGrantResourceOwnerCredentials to support grant_type=password.
                    OnGrantResourceOwnerCredentials = context => {
                        // Do your credentials validation here.
                        // Note: you can call Rejected() with a message
                        // to indicate that authentication failed.

                        var identity = new ClaimsIdentity(OpenIdConnectDefaults.AuthenticationScheme);
                        identity.AddClaim(ClaimTypes.NameIdentifier, "todo");

                        // By default, claims are not serialized in the access and identity tokens.
                        // Use the overload taking a "destination" to make sure your claims
                        // are correctly inserted in the appropriate tokens.
                        identity.AddClaim("urn:customclaim", "value", "token id_token");

                        var ticket = new AuthenticationTicket(
                            new ClaimsPrincipal(identity),
                            new AuthenticationProperties(),
                            context.Options.AuthenticationScheme);

                        // Call SetResources with the list of resource servers
                        // the access token should be issued for.
                        ticket.SetResources(new[] { "resource_server_1" });

                        // Call SetScopes with the list of scopes you want to grant
                        // (specify offline_access to issue a refresh token).
                        ticket.SetScopes(new[] { "profile", "offline_access" });

                        context.Validated(ticket);

                        return Task.FromResult<object>(null);
                    }
                }
            });

            app.UseMvc();
        }
    }

project.json

    {
      "dependencies": {
        "Microsoft.AspNet.Server.WebListener": "1.0.0-rc1-final",
        "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
        "Microsoft.AspNet.Authentication.JwtBearer": "1.0.0-rc1-final",
        "AspNet.Security.OpenIdConnect.Server": "1.0.0-beta4"
      }
    }
Share:
15,404
Son_of_Sam
Author by

Son_of_Sam

Father, Husband and Coder (all hobbies are in here) and should be the order of life. Son Of Sam is just a bad nick name from my time playing counter strike sorry about that. Love VB because it make sense #SOreadytohelp

Updated on June 05, 2022

Comments

  • Son_of_Sam
    Son_of_Sam almost 2 years

    I getting the following error with a valid token after the application re-start or publish

    IDX10503: Signature validation failed. Keys tried: 'System.IdentityModel.Tokens.RsaSecurityKey
    Exceptions caught:
    token: '{"typ":"JWT","alg":"RS256","kid":null}.{"unique_name":"[email protected]","iss":"XXXXXX","aud":"XXXXX","exp":1444876186}'
    

    This is the function that generate the KEY

    private void generateRsaKeys()
    {
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048))
        {
    
            key = new RsaSecurityKey(rsa.ExportParameters(true));
            credentials = new SigningCredentials (key,SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);
            rsa.PersistKeyInCsp = true;
        }
    }
    

    This is how the configuration is done

    services.ConfigureOAuthBearerAuthentication(options =>
    {
        options.AutomaticAuthentication = true;
        options.TokenValidationParameters.IssuerSigningKey = generateRsaKeys();
        options.TokenValidationParameters.ValidAudience = audience;
        options.TokenValidationParameters.ValidIssuer = issuer;
    
    });
    
    app.UseStaticFiles();
    app.UseOAuthBearerAuthentication();
    
    // Add MVC to the request pipeline.
    app.UseMvc();
    

    and this is the action on my controller

    // POST: /token
    [HttpPost()]
    public async Task<IActionResult> Token([FromBody] LoginModel model)
    {
        if (!ModelState.IsValid)
            return HttpBadRequest();
    
        JwtSecurityTokenHandler handler = _bearerOptions.SecurityTokenValidators.OfType<JwtSecurityTokenHandler>().First();
    
        try
        {
            var user = await _Repo.GetDetailAsync(model.Email);
            if (!model.Password.Equals(user.Password))
                return HttpUnauthorized();
    
            JwtSecurityToken securityToken = handler.CreateToken
            (
                issuer: _bearerOptions.TokenValidationParameters.ValidIssuer,
                audience: _bearerOptions.TokenValidationParameters.ValidAudience,
                signingCredentials: _signingCredentials,
                subject: new ClaimsIdentity(new Claim[] { new Claim(ClaimTypes.Name, user.Email) }),
                expires: DateTime.Now.AddMinutes(2)
            );
    
            string token = handler.WriteToken(securityToken);
    
            return new ObjectResult(new TokenModel() { AccessToken = token, TokenType = "bearer" });
    
        }
        catch (Exception ex)
        {
            // TODO: add loggin logic here 
            return HttpUnauthorized();
        }
    
    }