本文へスキップ
ecdsa.com

JWT の ES256 と EdDSA(Ed25519)の比較

どちらも 64 バイトの署名を生成し、トークンもほぼ同じになります。判断のポイントは、ライブラリ、キーストア、トークンの利用側がそれぞれどちらに対応しているかと、Ed25519 の新しい alg 名です。

JWT で ES256 と EdDSA のどちらを使うかは、ECDSA と EdDSA の一般的な比較よりも範囲の狭い問題です。トークンのサイズは同じで、どちらも高速であり、新しい発行者にはどちらも RS256 より推奨されます。違うのは対応状況です。ES256 は多くのプラットフォームやキーストアで必須とされているか、前提とされています。一方 Ed25519 は正しく実装するのが比較的簡単で、JOSE では現在 EdDSA ではなく Ed25519 という名前になっています。

同じ判断のうち RSA に関する部分は ES256 と RS256 のガイドで扱っています。このページでは、楕円曲線を使う 2 つの選択肢を比較します。

比較表

ES256EdDSA / Ed25519
JOSE の algES256(RFC 7518 §3.4)Ed25519(RFC 9864)。従来の EdDSA(RFC 8037)は現在非推奨
曲線とハッシュP-256 と SHA-256Edwards25519 と SHA-512
JWKkty: EC、crv: P-256、x、ykty: 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)
クラウド KMSAWS、Google、Azure:対応AWS(2025年11月以降)と Google:対応。Azure:非対応
ブラウザーの WebCryptoすべてのブラウザーFirefox 129、Safari 17、Chrome 137 以降

サイズと速度

どちらの署名も 64 バイトなので、トークンの長さはヘッダーを除けば同じです。以下のスクリプトは Node.js 26.10 で実行したもので、node:crypto だけを使い、同じクレームを両方の方式で署名します(実際のコードでは JOSE ライブラリを使うべきです)。

Node.js:同じペイロードに対する ES256 と Ed25519 の JWT
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、政府系、金融系):そのプロファイルに記載されているもの

移行時の注意点

  1. 固有の kid と明示的な alg を付けて新しい鍵を JWKS に追加し、利用側がキャッシュを更新する時間を確保します。
  2. 検証者を鍵ごとに設定する:各鍵で受け付けるアルゴリズムは厳密に 1 つにします(Ed25519 では、利用側によって異なる場合は EdDSA と Ed25519 の両方のヘッダー値)。この対応付けなしに、トークンの alg をそのまま受け入れてはいけません。
  3. 発行者を新しい鍵に切り替えます。既存のトークンは、期限切れになるまで古い鍵で検証され続けます。
  4. トークンの最長の有効期間が過ぎたら、古い鍵を削除します。
  5. エンコーディングをエンドツーエンドでテストする:KMS から得た ES256 署名は、トークンに入れる前に DER から生形式に変換しなければなりません。DER ⇄ 生形式の変換ツールと JWT デバッガーで、トークンの中身を確認できます。

参考資料

すべての比較