本文へスキップ
ecdsa.com
.NET

Microsoft.IdentityModel (JsonWebTokenHandler)

Microsoft.IdentityModel の JsonWebTokenHandler は、ASP.NET Core の認証と Microsoft Entra ID の SDK の内部で使われる JWT バリデーターです。デフォルトで 5 分の時刻のずれを許容し、exp を必須とし、P-384 の鍵による ES256 トークンも受け入れます。
ライセンス
MIT
確認したバージョン
8.23.0 ·
ソースコード
github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet
ドキュメント
learn.microsoft.com/dotnet/api/microsoft.identitymodel.jsonwebtokens.jsonwebtokenhandler

互換性の概要

JOSE と JWT

JWS アルゴリズム
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

Microsoft.IdentityModel.JsonWebTokens は、JsonWebTokenHandler と TokenValidationParameters オブジェクトで JWT を作成・検証します。ASP.NET Core の AddJwtBearer はリクエストをこのハンドラーに通すため、そのデフォルトがほとんどの .NET API のデフォルトになります。8.23.0 では HS、RS、PS、ES の各ファミリーで署名と検証ができました。このパッケージには EdDSA 用のセキュリティキーの型はありません。

ValidAlgorithms はデフォルトで null で、その場合は鍵によって決まります。EC 鍵に対しては、RS256 トークンも、EC 鍵の PEM テキストで偽造した HS256 トークンも、IDX10517: Signature validation failed. で失敗しました。署名のないトークンは、RequireSignedTokens を false に設定しない限り IDX10504 で失敗します。足りないのは曲線の結び付けです。P-384 の鍵で ES256 の署名ができ、同じハンドラーはその結果のトークンを検証で受け入れました。jose、PyJWT、golang-jwt といった他のライブラリはこれを拒否します。サンプルのように、ValidAlgorithms を設定し、正しいサイズの鍵を使ってください。

有効期間のルールはほとんどのライブラリと逆です。exp は必須(IDX10225)で、TokenValidationParameters.DefaultClockSkew は 5 分なので、トークンは期限切れ後も最長 5 分間は使えます。有効期間の短いアクセストークンにとってはかなり長い許容時間です。ClockSkew を明示的に設定してください。CreateToken は、Expires を設定しなくても exp を追加します。

ES256 署名は JWS が求める 64 バイトの生の r‖s で、.NET 自身の ECDsa のデフォルトと一致します。DER 署名は、偽造された署名と同じ IDX10517 で失敗します。エラーメッセージでは、デバッグ時に IdentityModelEventSource.ShowPII を有効にしない限り、トークンの内容が [PII of type ... is hidden] として隠されます。代わりに JWT デバッガーでトークンをデコードし、プロバイダーの鍵は JWKS チェッカーで確認してください。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/microsoft-identitymodel/run.sh がコンテナイメージ mcr.microsoft.com/dotnet/sdk:10.0.402-noble 内で Microsoft.IdentityModel (JsonWebTokenHandler) 8.23.0 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • JWS アルゴリズム

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512

    256/384/512 の HS、RS、PS、ES で署名と検証ができました。パッケージに EdDSA の鍵の型はありません。

    チェックによる記録
    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
  • ES256 署名のエンコーディング

    raw

    ES256 署名は 64 バイトの生の r‖s です。

    チェックによる記録
    fact jws-ecdsa-encoding = raw
  • JWS 内の DER 署名

    rejected

    DER エンコードの ES256 署名は IDX10517: Signature validation failed. で失敗します。

    チェックによる記録
    fact jws-der-signature = rejected
    evidence jws-der-signature: SecurityTokenSignatureKeyNotFoundException: IDX10517: Signature validation failed. The token's kid is missing. Keys tried:
  • アルゴリズムの許可リスト

    from-key

    ValidAlgorithms が null の場合は鍵によって決まります。EC 鍵に対して RS256 トークンを検証すると IDX10517 で失敗しました。

    チェックによる記録
    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:
  • 署名なしトークン(alg none)

    opt-in

    署名のないトークンはデフォルトでは IDX10504: Unable to validate signature, token does not have a signature で失敗し、RequireSignedTokens = false では検証を通ります。

    チェックによる記録
    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
  • HMAC シークレットとして使われる公開鍵

    rejected

    PEM テキストを鍵とした HS256 トークンは、EC 鍵に対して IDX10517 で失敗しました。

    チェックによる記録
    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:
  • alg と曲線の照合

    not-enforced

    CreateToken は P-384 の鍵で ES256 の署名を行い、その鍵と ES256 に設定した ValidAlgorithms による検証も成功しました。

    チェックによる記録
    fact jwt-ecdsa-curve-check = not-enforced
    evidence jwt-ecdsa-curve-check: ES256 with a P-384 key: signed; validation: valid
  • デフォルトのクロックスキュー

    300s

    TokenValidationParameters.DefaultClockSkew は 5 分です。最大で約 300 秒前に期限切れになったトークンも検証を通りました。

    チェックによる記録
    fact jwt-clock-skew = 300s
    evidence jwt-clock-skew: TokenValidationParameters.DefaultClockSkew = 00:05:00
  • 有効期限クレームの要否

    yes

    exp のないトークンは SecurityTokenNoExpirationException: IDX10225 で失敗します。

    チェックによる記録
    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:
  • デフォルトで非表示のエラー詳細

    yes

    検証エラーでは、トークンの内容が [PII of type ... is hidden] に置き換えられます。IdentityModelEventSource.ShowPII = true を設定すると、このプレースホルダーは表示されなくなりました。

    チェックによる記録
    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

使い方

ES256 トークンを作成し、アルゴリズム、発行者、オーディエンスを固定して、時刻のずれの許容を 30 秒に減らして検証します。
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"]}");

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ mcr.microsoft.com/dotnet/sdk:10.0.402-noble 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
dotnet new console --force -o . >/dev/null && dotnet add package Microsoft.IdentityModel.JsonWebTokens --version 8.23.0 >/dev/null
コマンド
rm -f Program.cs && DOTNET_NOLOGO=1 dotnet run 2>/dev/null
一致すべき出力(正規表現)
valid: True, sub: user-42

ライブラリ