JWK
暗号鍵を JSON 形式で表現する標準仕様で、JWT の署名検証に使用される
JWK とは
JWK (JSON Web Key) は、暗号鍵を JSON 形式で表現する標準仕様 (RFC 7517) である。JWT の署名検証に使う公開鍵を、HTTP エンドポイント経由で安全に配布するために広く使われている。
従来、公開鍵の配布は PEM ファイルの手動コピーや X.509 証明書の配布に依存していた。JWK はこれを JSON + HTTPS という Web ネイティブな方式に置き換え、鍵のローテーションや複数鍵の管理を自動化可能にした。OAuth 2.0 / OpenID Connect のエコシステムで事実上の標準となっている。
JWK の構造
{
"kty": "RSA",
"kid": "2026-03-key-1",
"use": "sig",
"alg": "RS256",
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2...",
"e": "AQAB"
}
| フィールド | 意味 | 例 |
|---|---|---|
kty |
鍵の種類 (Key Type) | RSA, EC, OKP |
kid |
鍵の識別子 (Key ID) | 任意の文字列。JWT ヘッダーの kid と照合 |
use |
用途 | sig (署名), enc (暗号化) |
alg |
アルゴリズム | RS256, ES256 |
n, e |
RSA 公開鍵のパラメータ | Base64url エンコードされた値 |
kid が最も重要なフィールドだ。JWT を受け取ったサーバーは、JWT ヘッダーの kid と JWKS 内の kid を照合し、どの鍵で署名検証すべきかを特定する。
JWKS エンドポイント
認証プロバイダーは、公開鍵のセットを JWKS (JSON Web Key Set) エンドポイントで公開する。
# Cognito
https://cognito-idp.ap-northeast-1.amazonaws.com/{userPoolId}/.well-known/jwks.json
# Auth0
https://{tenant}.auth0.com/.well-known/jwks.json
# Google
https://www.googleapis.com/oauth2/v3/certs
レスポンスは keys 配列に複数の JWK を含む。
{
"keys": [
{ "kid": "key-1", "kty": "RSA", "use": "sig", ... },
{ "kid": "key-2", "kty": "RSA", "use": "sig", ... }
]
}
複数の鍵が含まれるのは、鍵のローテーション中に新旧両方の鍵が有効である必要があるためだ。
JWT 署名検証の流れ
- クライアントが JWT をサーバーに送信する
- サーバーは JWT ヘッダーから
kidを取得する - JWKS エンドポイントから公開鍵セットを取得する (キャッシュがあればキャッシュから)
kidが一致する JWK を見つける- その公開鍵で JWT の署名を検証する
- 署名が有効なら、JWT のペイロード (claims) を信頼する
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: `https://cognito-idp.ap-northeast-1.amazonaws.com/${userPoolId}/.well-known/jwks.json`,
cache: true, // JWKS をキャッシュ
cacheMaxAge: 600000, // 10分間キャッシュ
});
function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
client.getSigningKey(header.kid!, (err, key) => {
callback(err, key?.getPublicKey());
});
}
jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) throw new Error('JWT 検証失敗');
// decoded にペイロードが入る
});
鍵のローテーション
認証プロバイダーは定期的に署名鍵をローテーションする。Cognito は自動的にローテーションを行い、JWKS エンドポイントには常に新旧両方の鍵が含まれる。
ローテーションの流れは以下の通りだ。
- 新しい鍵ペア (key-2) を生成し、JWKS に追加する
- 新しい JWT は key-2 で署名する
- 古い鍵 (key-1) で署名された JWT がまだ有効期限内なので、key-1 も JWKS に残す
- key-1 で署名された JWT が全て期限切れになったら、key-1 を JWKS から削除する
クライアント側の実装で重要なのは、JWKS をキャッシュしつつ、未知の kid に遭遇したら JWKS を再取得するロジックだ。キャッシュだけに頼ると、ローテーション直後に検証が失敗する。
API Gateway での JWT 検証
API Gateway (HTTP API) の JWT オーソライザーを使えば、Lambda 側で検証ロジックを実装する必要がない。
API Gateway が自動的に行う処理:
- JWKS エンドポイントから公開鍵を取得・キャッシュ
- JWT の署名検証
exp(有効期限) のチェックiss(発行者) とaud(対象者) の検証
Lambda には検証済みの claims がイベントオブジェクトに含まれて渡される。これにより、Lambda は認証ロジックを一切持たず、ビジネスロジックに集中できる。
RSA と EC の選択
JWK で使われる鍵の種類は主に RSA と EC (楕円曲線) の 2 つだ。
| 観点 | RSA (RS256) | EC (ES256) |
|---|---|---|
| 鍵サイズ | 2048 bit (256 bytes) | 256 bit (32 bytes) |
| 署名サイズ | 256 bytes | 64 bytes |
| 検証速度 | 速い | やや遅い |
| 署名速度 | 遅い | 速い |
| 互換性 | ほぼ全てのライブラリが対応 | 一部の古いライブラリで非対応 |
Cognito や Auth0 はデフォルトで RSA を使用する。JWT のサイズを小さくしたい場合 (モバイルアプリなど帯域が限られる環境) は EC を検討する価値がある。
よくある誤解
「JWKS エンドポイントは秘密にすべき」
JWKS エンドポイントは公開鍵を配布するためのものであり、秘密にする必要はない。公開鍵は文字通り「公開」するための鍵だ。秘密にすべきなのは署名に使う秘密鍵であり、これは認証プロバイダーの内部に保持される。
「JWT を検証すれば認可も完了」
JWT の署名検証は認証 (このトークンは本物か) を確認するだけだ。認可 (このユーザーにこの操作を許可するか) は、JWT のペイロードに含まれる claims (roles, scopes) を基に、アプリケーション側で別途判断する必要がある。
体系的に学ぶなら関連書籍を参照してほしい。