Zum Inhalt springen
ecdsa.com
.NET

Microsoft.IdentityModel (JsonWebTokenHandler)

JsonWebTokenHandler aus Microsoft.IdentityModel, der JWT-Validator, auf dem die Authentifizierung von ASP.NET Core und die SDKs für Microsoft Entra ID aufbauen: standardmäßig fünf Minuten Toleranz für Uhrenabweichungen, exp als Pflicht-Claim und ES256-Tokens, die auch mit P-384-Schlüsseln akzeptiert werden.
Lizenz
MIT
Geprüfte Version
8.23.0 ·
Quellcode
github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
Dokumentation
learn.microsoft.com/dotnet/api/microsoft.identitymodel.jsonwebtokens.jsonwebtokenhandler

Kompatibilität auf einen Blick

JOSE und JWT

JWS-Algorithmen
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

Microsoft.IdentityModel.JsonWebTokens erstellt und validiert JWTs mit JsonWebTokenHandler und einem Objekt TokenValidationParameters; AddJwtBearer in ASP.NET Core leitet Anfragen durch diesen Handler, sodass seine Standardeinstellungen die der meisten .NET-APIs sind. Version 8.23.0 signierte und validierte mit den Familien HS, RS, PS und ES; einen Sicherheitsschlüsseltyp für EdDSA gibt es im Paket nicht.

ValidAlgorithms ist standardmäßig null, und der Schlüssel entscheidet: Ein RS256-Token und ein HS256-Token, das mit dem PEM-Text des EC-Schlüssels gefälscht wurde, scheiterten beide an einem EC-Schlüssel mit IDX10517: Signature validation failed. Unsignierte Tokens scheitern mit IDX10504, sofern RequireSignedTokens nicht auf false gesetzt ist. Die Lücke liegt bei der Bindung an die Kurve: Mit einem P-384-Schlüssel ließ sich ES256 signieren, und derselbe Handler validierte das resultierende Token – was andere Bibliotheken wie jose, PyJWT und golang-jwt ablehnen. Setze ValidAlgorithms und verwende Schlüssel der richtigen Größe, wie es das Beispiel tut.

Bei der Gültigkeitsdauer verhält sich die Bibliothek umgekehrt wie die meisten anderen: exp ist Pflicht (IDX10225), und TokenValidationParameters.DefaultClockSkew beträgt fünf Minuten, sodass ein Token nach seinem Ablauf noch bis zu fünf Minuten lang funktioniert. Für kurzlebige Access Tokens ist das großzügig; setze ClockSkew explizit. CreateToken fügt exp auch dann hinzu, wenn Expires nicht gesetzt ist.

ES256-Signaturen sind das 64 Byte lange rohe r‖s, das JWS verlangt, passend zum Standard der .NET-eigenen Klasse ECDsa; eine DER-Signatur scheitert mit demselben IDX10517 wie eine Fälschung. Fehlermeldungen verbergen den Inhalt des Tokens als [PII of type ... is hidden], sofern IdentityModelEventSource.ShowPII beim Debuggen nicht aktiviert ist – dekodiere das Token stattdessen im JWT-Debugger und prüfe die Schlüssel eines Anbieters mit dem JWKS-Checker.

Fakten und die Prüfungen dahinter

Nichts im Folgenden ist aus einer Dokumentation abgeschrieben. Das Prüfskript content/libraries/verify/microsoft-identitymodel/run.sh hat Microsoft.IdentityModel (JsonWebTokenHandler) 8.23.0 am 20. Oktober 2026 im Container-Image mcr.microsoft.com/dotnet/sdk:10.0.402-noble ausgeführt; die Seite muss mit seiner Ausgabe übereinstimmen, und jeder Fakt führt die zugehörigen Zeilen auf.

  • JWS-Algorithmen

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

    Signieren und Validieren funktionierten mit HS, RS, PS und ES bei 256/384/512; im Paket gibt es keinen Schlüsseltyp für EdDSA.

    Von der Prüfung aufgezeichnet
    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
  • Kodierung von ES256-Signaturen

    raw

    ES256-Signaturen bestehen aus 64 Byte rohem r‖s.

    Von der Prüfung aufgezeichnet
    fact jws-ecdsa-encoding = raw
  • DER-Signatur in einer JWS

    rejected

    Eine DER-kodierte ES256-Signatur scheitert mit IDX10517: Signature validation failed.

    Von der Prüfung aufgezeichnet
    fact jws-der-signature = rejected
    evidence jws-der-signature: SecurityTokenSignatureKeyNotFoundException: IDX10517: Signature validation failed. The token's kid is missing. Keys tried:
  • Allowlist der Algorithmen

    from-key

    Ist ValidAlgorithms null, entscheidet der Schlüssel: Ein RS256-Token scheiterte an einem EC-Schlüssel mit IDX10517.

    Von der Prüfung aufgezeichnet
    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:
  • Unsignierte Tokens (alg none)

    opt-in

    Unsignierte Tokens scheitern standardmäßig mit IDX10504: Unable to validate signature, token does not have a signature und werden mit RequireSignedTokens = false validiert.

    Von der Prüfung aufgezeichnet
    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
  • Öffentlicher Schlüssel als HMAC-Secret

    rejected

    Ein HS256-Token mit dem PEM-Text als Schlüssel scheiterte am EC-Schlüssel mit IDX10517.

    Von der Prüfung aufgezeichnet
    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:
  • Abgleich der Kurve mit alg

    not-enforced

    CreateToken signierte ES256 mit einem P-384-Schlüssel, und die Validierung mit diesem Schlüssel und ValidAlgorithms auf ES256 war erfolgreich.

    Von der Prüfung aufgezeichnet
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key: signed; validation: valid
  • Standardtoleranz für Uhrabweichungen

    300s

    TokenValidationParameters.DefaultClockSkew beträgt 5 Minuten: Tokens, die bis zu etwa 300 Sekunden zuvor abgelaufen waren, wurden noch validiert.

    Von der Prüfung aufgezeichnet
    fact jwt-clock-skew = 300s
    evidence jwt-clock-skew: TokenValidationParameters.DefaultClockSkew = 00:05:00
  • Ablauf-Claim erforderlich

    yes

    Ein Token ohne exp scheitert mit SecurityTokenNoExpirationException: IDX10225.

    Von der Prüfung aufgezeichnet
    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:
  • Fehlerdetails standardmäßig verborgen

    yes

    Validierungsfehler ersetzen den Inhalt des Tokens durch [PII of type ... is hidden]; mit IdentityModelEventSource.ShowPII = true verschwand der Platzhalter.

    Von der Prüfung aufgezeichnet
    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

Verwendung

Erstelle ein ES256-Token und validiere es: Algorithmus, Aussteller und Audience sind festgelegt, die Toleranz für Uhrenabweichungen ist auf 30 Sekunden reduziert.
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"]}");

Die Snippet-Testumgebung führt jedes Beispiel unverändert im Container-Image mcr.microsoft.com/dotnet/sdk:10.0.402-noble aus, und der Lauf schlägt fehl, wenn die Ausgabe nicht übereinstimmt.

Einrichtung
dotnet new console --force -o . >/dev/null && dotnet add package Microsoft.IdentityModel.JsonWebTokens --version 8.23.0 >/dev/null
Befehl
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
Ausgabe muss passen (regulärer Ausdruck)
valid: True, sub: user-42

Bibliotheken