JWT で ES256 と EdDSA のどちらを使うかは、ECDSA と EdDSA の一般的な比較よりも範囲の狭い問題です。トークンのサイズは同じで、どちらも高速であり、新しい発行者にはどちらも RS256 より推奨されます。違うのは対応状況です。ES256 は多くのプラットフォームやキーストアで必須とされているか、前提とされています。一方 Ed25519 は正しく実装するのが比較的簡単で、JOSE では現在 EdDSA ではなく Ed25519 という名前になっています。
同じ判断のうち RSA に関する部分は ES256 と RS256 のガイドで扱っています。このページでは、楕円曲線を使う 2 つの選択肢を比較します。
比較表
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
JOSE の alg | ES256(RFC 7518 §3.4) | Ed25519(RFC 9864)。従来の EdDSA(RFC 8037)は現在非推奨 |
| 曲線とハッシュ | P-256 と SHA-256 | Edwards25519 と SHA-512 |
| JWK | kty: EC、crv: P-256、x、y | kty: OKP、crv: Ed25519、x |
| 公開鍵の JWK(実測、コンパクトな JSON) | 126 文字 | 79 文字 |
| トークン内の署名 | 生の r‖s で 64 バイト、86 文字 | 64 バイト、86 文字 |
| トークンの例(後述の実測値) | 232 文字 | 234 文字(alg の値が長いため) |
| 決定的 | いいえ(多くのライブラリではランダムな nonce) | はい |
| JWA の要件レベル | 推奨+(Recommended+) | 任意(Optional) |
| COSE の ID | −7(または RFC 9864 の −9 ESP256) | −19(従来は −8) |
| クラウド KMS | AWS、Google、Azure:対応 | AWS(2025年11月以降)と Google:対応。Azure:非対応 |
| ブラウザーの WebCrypto | すべてのブラウザー | Firefox 129、Safari 17、Chrome 137 以降 |
サイズと速度
どちらの署名も 64 バイトなので、トークンの長さはヘッダーを除けば同じです。以下のスクリプトは Node.js 26.10 で実行したもので、node:crypto だけを使い、同じクレームを両方の方式で署名します(実際のコードでは JOSE ライブラリを使うべきです)。
import { generateKeyPairSync, sign } from "node:crypto";
const b64url = (value) => Buffer.from(value).toString("base64url");
const payload = b64url(JSON.stringify({ iss: "https://auth.example.com", sub: "user-42", aud: "api", exp: 1792500000 }));
const es = generateKeyPairSync("ec", { namedCurve: "P-256" });
const esInput = `${b64url(JSON.stringify({ alg: "ES256", kid: "es-1" }))}.${payload}`;
const es256 = `${esInput}.${b64url(sign("sha256", Buffer.from(esInput), { key: es.privateKey, dsaEncoding: "ieee-p1363" }))}`;
const ed = generateKeyPairSync("ed25519");
const edInput = `${b64url(JSON.stringify({ alg: "Ed25519", kid: "ed-1" }))}.${payload}`;
const ed25519 = `${edInput}.${b64url(sign(null, Buffer.from(edInput), ed.privateKey))}`;
console.log("ES256 token", es256.length, "chars, public JWK", JSON.stringify(es.publicKey.export({ format: "jwk" })).length, "chars");
console.log("Ed25519 token", ed25519.length, "chars, public JWK", JSON.stringify(ed.publicKey.export({ format: "jwk" })).length, "chars");
ES256 token 232 chars, public JWK 126 chars
Ed25519 token 234 chars, public JWK 79 chars
速度は決め手になりません。Apple M4 Max の 1 コアで、OpenSSL 3.6.5 は毎秒約 87,000 回の P-256 署名と約 60,000 回の Ed25519 署名を生成します。OKP 鍵の座標は 1 つだけなので、Ed25519 鍵を含む JWKS のほうが小さくなります。
エンコーディングと正しさ
JWS の ES256 署名は、64 バイトの生の r‖s 形式でなければなりません(RFC 7518 §3.4)。多くの ECDSA API はデフォルトで DER を使います。OpenSSL、Java の SHA256withECDSA、Go の ecdsa.SignASN1、dsaEncoding を指定しない Node の crypto.sign()、AWS KMS と Google Cloud KMS の署名 API がそうです。変換を忘れた自作の ES256 コードは 70〜72 バイトの署名を生成し、仕様に準拠した検証者はこれを拒否します。逆の誤りでは、正しいトークンの検証が失敗します。ECDSA の検証者は、0 に等しい r や s も拒否しなければなりません。Java 15〜18 はこれを行っておらず(CVE-2022-21449)、修正されるまで誰でも ES256 トークンを偽造できる状態でした。
Ed25519 のエンコーディングは 64 バイトの 1 種類だけで、署名は決定的であり、扱いを誤るおそれのある nonce もありません。落とし穴は別のところにあります。秘密鍵を 32 バイトのシードとして扱うライブラリもあれば 64 バイトとして扱うライブラリもあり、古い JOSE ライブラリは "alg": "EdDSA" しか認識しません。
alg 名の変更
RFC 8037(2017年)は、EdDSA を、曲線が鍵によって決まる(crv: Ed25519 または Ed448)ポリモーフィックな JOSE アルゴリズムとして登録しました。RFC 9864(2025年10月)はポリモーフィックな識別子を非推奨とし、Ed25519 と Ed448 を登録しました。これにより、ES256 が当初からそうであったように、alg だけで処理が決まります。2026年時点で対応状況はまちまちです。jose 6 は EdDSA と Ed25519 の両方を受け付け、joserfc と Nimbus JOSE+JWT は新しい名前に対応していますが、PyJWT 2.15 と golang-jwt v5 は今も EdDSA しか受け付けません。多くの関係者が利用するトークンでは、ヘッダーの値を切り替える前に利用側を 1 つずつ確認してください。移行期間中は、検証者が同じ鍵に対して両方の名前を受け付けるようにできます。
プラットフォームとキーストアの対応状況
ES256 は最大公約数的な選択肢です。DPoP プルーフ(RFC 9449)では通常の選択であり、FAPI 2.0 が認める 3 つのアルゴリズム(PS256、ES256、Ed25519 を使う EdDSA)の 1 つです。Apple の App Store Server API と APNs のプロバイダートークンでは必須で、すべての OpenID Connect プロバイダーが対応しており、WebAuthn ではデフォルトの COSE アルゴリズムです。HSM、TPM、Secure Enclave、クラウド KMS はいずれも P-256 鍵を提供しています。
ソフトウェアでは Ed25519 も追いついています。Node.js、Go、Java 15 以降、ライブラリ経由の .NET、Python の cryptography、ブラウザーの WebCrypto、主要な JOSE ライブラリが対応しています。キーストアでの対応はより新しく、AWS KMS は 2025年11月に Ed25519 の署名鍵を追加し、Google Cloud KMS は EC_SIGN_ED25519 を提供していますが、一部の HSM モデルと Azure Key Vault は対応していません。マネージドなキーストアの中でトークンに署名しなければならない場合は、まずそのアルゴリズム一覧を確認してください。
それぞれの用途
- 公開 ID プロバイダーで、リライングパーティが多数:ES256(必要に応じて、古い利用側のために RS256 も維持)
- 複数のクラウドにまたがる HSM や KMS に鍵を保管:対応状況が揃う ES256
- 内部サービスで両端を自分で管理し、最新のライブラリを使用:すべての検証者が対応していれば
alg: Ed25519の Ed25519、そうでなければEdDSA - ブラウザーとモバイルアプリからの送信者制約付きトークン(DPoP):どのプラットフォームのキーストアでもエクスポート不可の形で生成できる ES256
- アルゴリズムを指定しているプロファイル(FAPI、政府系、金融系):そのプロファイルに記載されているもの
移行時の注意点
- 固有の
kidと明示的なalgを付けて新しい鍵を JWKS に追加し、利用側がキャッシュを更新する時間を確保します。 - 検証者を鍵ごとに設定する:各鍵で受け付けるアルゴリズムは厳密に 1 つにします(Ed25519 では、利用側によって異なる場合は
EdDSAとEd25519の両方のヘッダー値)。この対応付けなしに、トークンのalgをそのまま受け入れてはいけません。 - 発行者を新しい鍵に切り替えます。既存のトークンは、期限切れになるまで古い鍵で検証され続けます。
- トークンの最長の有効期間が過ぎたら、古い鍵を削除します。
- エンコーディングをエンドツーエンドでテストする:KMS から得た ES256 署名は、トークンに入れる前に DER から生形式に変換しなければなりません。DER ⇄ 生形式の変換ツールと JWT デバッガーで、トークンの中身を確認できます。