Pular para o conteúdo
ecdsa.com
.NET

Microsoft.IdentityModel (JsonWebTokenHandler)

O JsonWebTokenHandler do Microsoft.IdentityModel, o validador de JWT por trás da autenticação do ASP.NET Core e dos SDKs do Microsoft Entra ID: cinco minutos de tolerância padrão para diferença de relógio, exp obrigatória e tokens ES256 aceitos com chaves P-384.
Licença
MIT
Versão verificada
8.23.0 ·
Código-fonte
github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
Documentação
learn.microsoft.com/dotnet/api/microsoft.identitymodel.jsonwebtokens.jsonwebtokenhandler

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

Microsoft.IdentityModel.JsonWebTokens cria e valida JWTs com JsonWebTokenHandler e um objeto TokenValidationParameters; o AddJwtBearer do ASP.NET Core passa as requisições por ele, o que faz dos seus padrões os padrões da maioria das APIs .NET. A versão 8.23.0 assinou e validou as famílias HS, RS, PS e ES; não existe no pacote um tipo de chave de segurança para EdDSA.

ValidAlgorithms é null por padrão, e a chave decide: um token RS256 e um token HS256 forjado com o texto PEM da chave EC falharam, ambos, contra uma chave EC com IDX10517: Signature validation failed. Tokens sem assinatura falham com IDX10504, a menos que RequireSignedTokens seja definido como false. A brecha está na vinculação à curva: o handler assinou ES256 com uma chave P-384, e o mesmo handler validou o token resultante, algo que outras bibliotecas — jose, PyJWT, golang-jwt — recusam. Defina ValidAlgorithms e use chaves do tamanho certo, como faz o exemplo.

As regras de validade são o oposto das da maioria das bibliotecas: exp é obrigatória (IDX10225) e TokenValidationParameters.DefaultClockSkew é de cinco minutos, então um token continua funcionando por até cinco minutos depois de expirar. Isso é generoso para tokens de acesso de vida curta; defina ClockSkew explicitamente. CreateToken adiciona exp mesmo quando Expires não é definido.

As assinaturas ES256 são o r‖s bruto de 64 bytes que o JWS exige, o mesmo padrão do próprio ECDsa do .NET; uma assinatura DER falha com o mesmo IDX10517 de uma assinatura forjada. As mensagens de erro ocultam o conteúdo do token como [PII of type ... is hidden], a menos que IdentityModelEventSource.ShowPII seja habilitado durante a depuração — em vez disso, decodifique o token no depurador de JWT e confira as chaves de um provedor com o verificador de JWKS.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/microsoft-identitymodel/run.sh executou Microsoft.IdentityModel (JsonWebTokenHandler) 8.23.0 na imagem de contêiner mcr.microsoft.com/dotnet/sdk:10.0.402-noble em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

    Assinatura e validação funcionaram com HS, RS, PS e ES em 256/384/512; não existe tipo de chave EdDSA no pacote.

    Registrado pela verificação
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512
    evidence jws-algorithms: EdDSA signing with an ECDsaSecurityKey or other built-in key: no EdDSA security key type
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER falha com IDX10517: Signature validation failed.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: SecurityTokenSignatureKeyNotFoundException: IDX10517: Signature validation failed. The token's kid is missing. Keys tried:
  • Lista de algoritmos permitidos

    from-key

    Com ValidAlgorithms null, a chave decide: um token RS256 validado com uma chave EC falhou com IDX10517.

    Registrado pela verificação
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: ValidAlgorithms null, RS256 token against the EC key: SecurityTokenSignatureKeyNotFoundException: IDX10517: Signature validation failed. The token's kid is missing. Keys tried:
  • Tokens sem assinatura (alg none)

    opt-in

    Por padrão, tokens sem assinatura falham com IDX10504: Unable to validate signature, token does not have a signature; com RequireSignedTokens = false, eles são validados.

    Registrado pela verificação
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: default parameters: SecurityTokenInvalidSignatureException: IDX10504: Unable to validate signature, token does not have a signature:
    evidence jwt-alg-none: RequireSignedTokens = false: valid
  • Chave pública usada como segredo HMAC

    rejected

    Um token HS256 que usava o texto PEM como chave falhou na validação com a chave EC, com IDX10517.

    Registrado pela verificação
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: HS256 token keyed with the PEM text, EC key, ValidAlgorithms null: SecurityTokenSignatureKeyNotFoundException: IDX10517: Signature validation failed. The token's kid is missing. Keys tried:
  • Curva conferida com o alg

    not-enforced

    CreateToken assinou ES256 com uma chave P-384, e a validação com essa chave e ValidAlgorithms definido como ES256 teve sucesso.

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key: signed; validation: valid
  • Tolerância padrão de diferença de relógio

    300s

    TokenValidationParameters.DefaultClockSkew é de 5 minutos: tokens expirados até cerca de 300 segundos antes ainda foram validados.

    Registrado pela verificação
    fact jwt-clock-skew = 300s
    evidence jwt-clock-skew: TokenValidationParameters.DefaultClockSkew = 00:05:00
  • Claim de expiração obrigatória

    yes

    Um token sem exp falha com SecurityTokenNoExpirationException: IDX10225.

    Registrado pela verificação
    fact jwt-exp-required = yes
    evidence jwt-exp-required: CreateToken without Expires adds exp anyway: True
    evidence jwt-exp-required: hand-built HS256 token without exp: SecurityTokenNoExpirationException: IDX10225: Lifetime validation failed. The token is missing an Expiration Time. Tokentype:
  • Detalhes do erro ocultos por padrão

    yes

    Os erros de validação substituem o conteúdo do token por [PII of type ... is hidden]; definir IdentityModelEventSource.ShowPII = true removeu esse marcador.

    Registrado pela verificação
    fact error-detail-hidden-by-default = yes
    evidence error-detail-hidden-by-default: PII placeholder in the default message: True; after IdentityModelEventSource.ShowPII = true: False

Uso

Crie um token ES256 e valide-o com algoritmo, emissor e público-alvo fixados e com a tolerância para diferença de relógio reduzida a 30 segundos.
using System.Security.Cryptography;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

var key = new ECDsaSecurityKey(ECDsa.Create(ECCurve.NamedCurves.nistP256)) { KeyId = "2026-10" };
var handler = new JsonWebTokenHandler();
var token = handler.CreateToken(new SecurityTokenDescriptor
{
    Issuer = "https://auth.example.com",
    Audience = "api",
    Claims = new Dictionary<string, object> { ["sub"] = "user-42" },
    Expires = DateTime.UtcNow.AddMinutes(10),
    SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256),
});

// Pin the algorithm (ES256 is not tied to P-256 here) and shrink the 5-minute default skew.
var result = await handler.ValidateTokenAsync(token, new TokenValidationParameters
{
    IssuerSigningKey = key,
    ValidIssuer = "https://auth.example.com",
    ValidAudience = "api",
    ValidAlgorithms = new[] { SecurityAlgorithms.EcdsaSha256 },
    ClockSkew = TimeSpan.FromSeconds(30),
});
Console.WriteLine($"valid: {result.IsValid}, sub: {result.Claims["sub"]}");

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner mcr.microsoft.com/dotnet/sdk:10.0.402-noble, e a execução falha se a saída não corresponder.

Configuração
dotnet new console --force -o . >/dev/null && dotnet add package Microsoft.IdentityModel.JsonWebTokens --version 8.23.0 >/dev/null
Comando
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
A saída deve corresponder a (expressão regular)
valid: True, sub: user-42

Bibliotecas