Выбор между ES256 и EdDSA для JWT — более узкий вопрос, чем выбор между ECDSA и EdDSA вообще. Размеры токенов одинаковые, обе схемы быстрые, и для новых издателей обе рекомендуются вместо RS256. Различается поддержка: ES256 требуют или ожидают многие платформы и хранилища ключей, а Ed25519 проще правильно реализовать, и в JOSE он теперь называется Ed25519, а не EdDSA.
Руководство по выбору между ES256 и RS256 разбирает ту же задачу со стороны RSA, а на этой странице сравниваются два варианта на эллиптических кривых.
Коротко о главном
| ES256 | EdDSA / Ed25519 | |
|---|---|---|
alg в JOSE | 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 символов |
| Подпись в токене | 64 байта в формате raw r‖s, 86 символов | 64 байта, 86 символов |
| Пример токена (замер ниже) | 232 символа | 234 символа (значение alg длиннее) |
| Детерминированность | Нет (в большинстве библиотек случайный nonce) | Да |
| Уровень требований JWA | Recommended+ (рекомендуется) | Optional (необязателен) |
| Идентификатор COSE | −7 (или −9 ESP256 по RFC 9864) | −19 (прежний −8) |
| Облачные KMS | AWS, Google, Azure: да | AWS (с ноября 2025 года) и 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 OpenSSL 3.6.5 создаёт около 87 000 подписей P-256 и 60 000 подписей Ed25519 в секунду. JWKS с ключами Ed25519 меньше, потому что у ключей OKP одна координата.
Кодировка и корректность
Подписи ES256 в JWS должны быть в формате raw r‖s длиной 64 байта (RFC 7518, § 3.4). Многие API для ECDSA по умолчанию выдают DER: OpenSSL, SHA256withECDSA в Java, ecdsa.SignASN1 в Go, crypto.sign() в Node без dsaEncoding, а также API подписи в AWS KMS и Google Cloud KMS. Самописный код для ES256, в котором забыли о преобразовании, выдаёт подписи длиной 70–72 байта, и проверяющие стороны, соблюдающие стандарт, их отклоняют, а обратная ошибка ломает проверку корректных токенов. Кроме того, проверяющие стороны ECDSA должны отклонять r или s, равные нулю; Java версий 15–18 их не отклоняла (CVE-2022-21449), и до выхода исправления кто угодно мог подделать токены ES256.
У Ed25519 одна 64-байтная кодировка, детерминированное подписание и нет nonce, с которым можно ошибиться. Ловушки у него в другом: одни библиотеки считают закрытым ключом 32-байтный seed, другие — 64 байта, а старые библиотеки JOSE распознают только "alg": "EdDSA".
Имя alg изменилось
RFC 8037 (2017) зарегистрировал EdDSA как полиморфный алгоритм JOSE, кривая которого определяется ключом (crv: Ed25519 или Ed448). RFC 9864 (октябрь 2025 года) объявляет полиморфные идентификаторы устаревшими и регистрирует Ed25519 и Ed448, так что операцию определяет один только alg, как это всегда было с ES256. В 2026 году поддержка неравномерна: jose 6 принимает и EdDSA, и Ed25519, joserfc и Nimbus JOSE+JWT поддерживают новые имена, а PyJWT 2.15 и golang-jwt v5 по-прежнему принимают только EdDSA. Если токены принимают многие стороны, проверьте каждого получателя, прежде чем менять значение в заголовке; на переходный период проверяющие стороны могут принимать оба имени для одного и того же ключа.
Поддержка в платформах и хранилищах ключей
ES256 — наименьший общий знаменатель. Его обычно выбирают для доказательств DPoP (RFC 9449), это один из трёх алгоритмов, которые допускает FAPI 2.0 (PS256, ES256 или EdDSA с Ed25519), он обязателен для App Store Server API от Apple и для токенов провайдера APNs, его поддерживают все поставщики OpenID Connect, и это алгоритм COSE по умолчанию в WebAuthn. Ключи P-256 предлагают все HSM, TPM, Secure Enclave и облачные KMS.
В программных реализациях Ed25519 догнал ES256: Node.js, Go, Java 15 и новее, .NET через библиотеки, cryptography в Python, WebCrypto в браузерах и основные библиотеки JOSE. В хранилищах ключей он появился позже: AWS KMS добавил ключи подписи Ed25519 в ноябре 2025 года, Google Cloud KMS предлагает EC_SIGN_ED25519, а некоторые модели HSM и Azure Key Vault его не поддерживают. Если токены нужно подписывать внутри управляемого хранилища ключей, сначала проверьте его список алгоритмов.
Что когда выбирать
- Публичный поставщик удостоверений, много доверяющих сторон: ES256 (при необходимости с RS256 для старых получателей).
- Ключи в HSM или KMS в нескольких облаках: ES256, чтобы поддержка была везде одинаковой.
- Внутренние сервисы, обе стороны под вашим контролем, современные библиотеки: Ed25519 с
alg: Ed25519, если его поддерживают все проверяющие стороны, иначеEdDSA. - Токены с привязкой к отправителю (DPoP) из браузеров и мобильных приложений: ES256, ключ для которого любое платформенное хранилище ключей может сгенерировать без возможности экспорта.
- Профили, которые явно задают алгоритмы (FAPI, государственные, финансовые): то, что указано в профиле.
Заметки о миграции
- Добавьте новый ключ в JWKS с собственным
kidи явно указаннымalgи дайте получателям время обновить кеши. - Настройте проверяющие стороны для каждого ключа отдельно: каждому ключу соответствует ровно один допустимый алгоритм (а для Ed25519 — оба значения заголовка,
EdDSAиEd25519, если ваши получатели поддерживают разные имена). Никогда не принимайтеalgиз токена без такой привязки. - Переключите издателя на новый ключ; уже выданные токены по-прежнему проверяются старым ключом, пока не истечёт их срок действия.
- Удалите старый ключ, когда пройдёт максимальный срок жизни токена.
- Проверьте кодировки на всём пути: подпись ES256 из KMS нужно преобразовать из DER в raw, прежде чем она попадёт в токен; конвертер DER и raw и отладчик JWT показывают, что находится в токене.
Источники
- RFC 7518, § 3.4, ES256 в JWS
- RFC 8037, кривые CFRG и EdDSA в JOSE
- RFC 9864, полностью определённые алгоритмы для JOSE и COSE
- RFC 8725, лучшие современные практики для JWT
- Справочник спецификаций ключей AWS KMS (ECC_NIST_EDWARDS25519)
- Назначения ключей и алгоритмы Google Cloud KMS
- FAPI 2.0 Security Profile, § 5.4.1, криптография
- CVE-2022-21449 (проверка подписей ECDSA в Java)