IDX10503:签名验证失败

IDX10503: Signature validation failed

在应用程序重新启动或发布后,我收到以下带有有效令牌的错误

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

这是生成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;
    }
}

这样配置就完成了

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();

这是我控制器上的操作

// 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();
    }

}

如果您每次(重新)启动服务器时都生成一个新的 RSA 密钥,那么这并不奇怪:无法使用 KEY B 验证使用 KEY A 签名的令牌。为了让您的场景正常工作,您需要将 RSA 密钥存储在某处并在启动期间使用相同的密钥。

一种方法是调用 rsa.ExportParameters(true) 并将不同的参数存储在某处,这样您就可以使用 rsa.ImportParameters(...).

轻松检索和导入它们

但你最好的选择是使用 AspNet.Security.OpenIdConnect.Server,它会在最新版本中自动为你生成并存储一个 RSA 密钥:

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"
      }
    }