ASP. NET Недопустимая подпись JWT ядра 3.1 при использовании AddJwtBearer () - PullRequest
1 голос
/ 27 февраля 2020

Проблема: AddJwtBearer() не удается, но проверка токена выполняется вручную.

Я пытаюсь сгенерировать и проверить JWT с асимметрией c RSA al go.

Я могу просто сгенерировать JWT, используя этот демонстрационный код

[HttpPost("[action]")]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<IActionResult> JwtBearerToken() {
    AppUser user = await userManager.GetUserAsync(User);

    using RSA rsa = RSA.Create(1024 * 2);
    rsa.ImportRSAPrivateKey(Convert.FromBase64String(configuration["jwt:privateKey"]), out int _);
    var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256);

    var jwt = new JwtSecurityToken(
        audience: "identityapp",
        issuer: "identityapp",
        claims: new List<Claim>() {new Claim(ClaimTypes.NameIdentifier, user.UserName)},
        notBefore: DateTime.Now,
        expires: DateTime.Now.AddHours(3),
        signingCredentials: signingCredentials
    );

    string token = new JwtSecurityTokenHandler().WriteToken(jwt);

    return RedirectToAction(nameof(Index), new {jwt = token});
}



Я также могу проверить токен и его подпись, используя демонстрационный код ниже

[HttpPost("[action]")]
[ValidateAntiForgeryToken]
public IActionResult JwtBearerTokenVerify(string token) {
    using RSA rsa = RSA.Create();
    rsa.ImportRSAPrivateKey(Convert.FromBase64String(configuration["jwt:privateKey"]), out int _);

    var handler = new JwtSecurityTokenHandler();
    ClaimsPrincipal principal = handler.ValidateToken(token, new TokenValidationParameters() {
        IssuerSigningKey = new RsaSecurityKey(rsa),
        ValidAudience = "identityapp",
        ValidIssuer = "identityapp",
        RequireExpirationTime = true,
        RequireAudience = true,
        ValidateIssuer = true,
        ValidateLifetime = true,
        ValidateAudience = true,
    }, out SecurityToken securityToken);

    return RedirectToAction(nameof(Index));
}



Но проверка не выполняется (401) при попадании в конечную точку, защищенную с помощью
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Сообщение об ошибке из заголовка HTTP: ошибка канала = "invalid_token ", error_description =" Подпись недействительна "



Моя конфигурация аутентификации носителя JWT находится здесь

.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => {
    using var rsa = RSA.Create();
    rsa.ImportRSAPrivateKey(Convert.FromBase64String(Configuration["jwt:privateKey"]), out int _);

    options.IncludeErrorDetails = true;
    options.TokenValidationParameters = new TokenValidationParameters() {
        IssuerSigningKey = new RsaSecurityKey(rsa),
        ValidAudience = "identityapp",
        ValidIssuer = "identityapp",
        RequireExpirationTime = true,
        RequireAudience = true,
        ValidateIssuer = true,
        ValidateLifetime = true,
        ValidateAudience = true,                 
    };
});

Я могу легко заставить ее работать с помощью симметри c ключ и HmacSha256 - но это не то, что я ищу.


ОБНОВЛЕНИЕ

Я написал исключение для ответ, и вот что я получаю:

IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: '', InternalId: '79b1afb2-0c85-43a1-bb81-e2accf9dff38'. , KeyId: 
'.
Exceptions caught:
 'System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'RSA'.
   at System.Security.Cryptography.RSAImplementation.RSACng.ThrowIfDisposed()
   at System.Security.Cryptography.RSAImplementation.RSACng.GetDuplicatedKeyHandle()
   at System.Security.Cryptography.RSAImplementation.RSACng.VerifyHash(ReadOnlySpan`1 hash, ReadOnlySpan`1 signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
   at System.Security.Cryptography.RSAImplementation.RSACng.VerifyHash(Byte[] hash, Byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.VerifyWithRsa(Byte[] bytes, Byte[] signature)
   at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.Verify(Byte[] bytes, Byte[] signature)
   at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.Verify(Byte[] input, Byte[] signature)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
'.
token: '{"alg":"RS256","typ":"JWT"}.{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"mail@mail.com","nbf":1582878368,"exp":1582889168,"iss":"identityapp","aud":"identityapp"}'.


ОБНОВЛЕНИЕ - Рабочее решение

Итак, я думаю, я понял это из сообщения об исключении. Ключ безопасности RSA был преждевременно утилизирован.

Я извлек ключ создания из AddJwtBearer() и вместо этого использовал внедрение зависимостей.

Кажется, это работает просто отлично. Но я не уверен, что это хорошая практика.

// Somewhere futher up in the ConfigureServices(IServiceCollection services) method
services.AddTransient<RsaSecurityKey>(provider => {
    RSA rsa = RSA.Create();
    rsa.ImportRSAPrivateKey(
        source: Convert.FromBase64String(Configuration["jwt:privateKey"]),
        bytesRead: out int _);

        return new RsaSecurityKey(rsa);
});


// Chaining onto services.AddAuthentication()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => {
    SecurityKey rsa = services.BuildServiceProvider().GetRequiredService<RsaSecurityKey>();

        options.IncludeErrorDetails = true;
        options.TokenValidationParameters = new TokenValidationParameters() {
        IssuerSigningKey = rsa,
        ValidAudience = "identityapp",
        ValidIssuer = "identityapp",
        RequireExpirationTime = true,
        RequireAudience = true,
        ValidateIssuer = true,
        ValidateLifetime = true,
        ValidateAudience = true,
    };

});

1 Ответ

0 голосов
/ 28 февраля 2020

используйте эту конфигурацию:

services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = false;
            x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(private_key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ClockSkew = TimeSpan.Zero
            };
        });
...