Перейти к содержимому
ecdsa.com
Python

JWCrypto

JWCrypto от Red Hat — реализация JOSE, которую используют FreeIPA и многие корпоративные сервисы на Python: полная поддержка JWS/JWE/JWK, включая ML-DSA, допуск на расхождение часов 60 секунд по умолчанию и alg: none только по явному запросу.
Лицензия
LGPL-3.0-or-later
Проверенная версия
1.6.1 ·
Исходный код
github.com/latchset/jwcrypto
Документация
jwcrypto.readthedocs.io/en/latest/

Совместимость вкратце

JOSE и JWT

Алгоритмы JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSAEd25519Ed448ML-DSA-44ML-DSA-65ML-DSA-87

JWCrypto реализует семейство JOSE поверх pyca/cryptography: JWK и наборы JWK, JWS, JWE и JWT. Всё построено на объектах ключей — jwk.JWK, созданных из PEM, JSON или через generate(), поэтому тип ключа явно виден при каждом вызове. Библиотека выпущена под лицензией LGPL-3.0-or-later; это важно, если вы распространяете изменённые копии.

Среди библиотек JOSE для Python у неё самый широкий набор алгоритмов: семейства HS, RS, PS и ES, ES256K, EdDSA и полностью определённые Ed25519 и Ed448, а в версии 1.6.1 ещё и ML-DSA-44, ML-DSA-65 и ML-DSA-87 с ключами AKP из черновика постквантового JOSE; в ходе проверки подпись и проверка подписи прошли для всех, и все они входят в список разрешённых по умолчанию. В этом списке по умолчанию и кроется подвох: если не передать algs, для подходящего ключа принимается любой из этих алгоритмов, поэтому сузьте список до тех, которые на самом деле использует ваш издатель, как это делает пример.

Неподписанные токены отклоняются с ошибкой Algorithm not allowed, если none нет в списке, а токен HS256, предъявленный с открытым ключом EC в формате JWK, не проходит проверку, потому что ключ этого типа не может выполнять HMAC. ES256 не принимает ключ P-384 и выдаёт Curve requested is "P-256", but key curve is "P-384". Подписи ES256 — это 64-байтовый формат raw r‖s; подпись DER не проходит проверку с ошибкой Verification failed for all signatures, а кодировку можно проверить в отладчике JWT.

Две настройки по умолчанию отличаются от принятых в других библиотеках. Проверка срока действия допускает расхождение часов в 60 секунд: токен, срок действия которого истёк 57 секунд назад, всё ещё прошёл проверку, а просроченный на 63 секунды — нет; это мягче, чем в PyJWT или jose, где расхождение не допускается вовсе. Кроме того, exp необязателен: токен без него прошёл проверку, если это утверждение не указано в check_claims.

Собирайте наборы ключей в конструкторе JWKS, а зашифрованные токены разбирайте в инспекторе JWE; размеры ключей и подписей ML-DSA показывает песочница постквантовых подписей.

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/jwcrypto/run.sh запустила JWCrypto 1.6.1 в среде cryptography 50.0.2 в образе контейнера python:3.14.8-slim-trixie; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Алгоритмы JWS

    HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSAEd25519Ed448ML-DSA-44ML-DSA-65ML-DSA-87

    Подпись и проверка работают для HS, RS, PS и ES на 256/384/512 бит, ES256K, EdDSA, Ed25519, Ed448 и ML-DSA-44/65/87 (с ключами AKP); все они входят в список разрешённых по умолчанию.

    Что записала проверка
    fact jws-algorithms = HS256, HS384, HS512, RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, EdDSA, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87
    evidence jws-algorithms: ML-DSA-44: JWK kty AKP
    evidence jws-algorithms: ML-DSA-65: JWK kty AKP
    evidence jws-algorithms: ML-DSA-87: JWK kty AKP
    evidence jws-algorithms: default allowed list: HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, EdDSA, ES256K, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87
  • Кодировка подписи ES256

    raw

    Подписи ES256 — 64 байта в формате raw r‖s.

    Что записала проверка
    fact jws-ecdsa-encoding = raw
  • Подпись в DER внутри JWS

    rejected

    Подпись ES256 в кодировке DER не проходит проверку с ошибкой InvalidJWSSignature: Verification failed for all signatures.

    Что записала проверка
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • Список разрешённых алгоритмов

    from-key

    Без algs действует список по умолчанию, а всё решает тип ключа: токен ES256 прошёл проверку с ключом EC, а проверка токена RS256 этим же ключом завершилась ошибкой.

    Что записала проверка
    fact jwt-algorithm-allowlist = from-key
    evidence jwt-algorithm-allowlist: no algs argument: verified; RS256 token with the EC key: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • Неподписанные токены (alg none)

    opt-in

    Неподписанный токен по умолчанию отклоняется с ошибкой Algorithm not allowed и проходит проверку только с algs=["none"].

    Что записала проверка
    fact jwt-alg-none = opt-in
    evidence jwt-alg-none: with the EC key: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSOperation('Algorithm not allowed')]"]
    evidence jwt-alg-none: algs ['none'] with an empty oct key: verified
  • Открытый ключ в роли секрета HMAC

    rejected

    Токен HS256 не прошёл проверку открытым ключом EC в формате JWK; проверку он прошёл только с намеренно созданным JWK типа oct, содержащим текст PEM.

    Что записала проверка
    fact jwt-hmac-public-key = rejected
    evidence jwt-hmac-public-key: HS256 token checked with the EC public JWK: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
    evidence jwt-hmac-public-key: only an explicit oct JWK made from the PEM text verifies it: True
  • Сверка кривой с alg

    enforced

    При подписи ES256 ключом P-384 выбрасывается Curve requested is "P-256", but key curve is "P-384".

    Что записала проверка
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidJWKValue: Curve requested is "P-256", but key curve is "P-384"
  • Допустимое расхождение часов по умолчанию

    60s

    Проверка срока действия по умолчанию допускает расхождение часов в 60 секунд: токен, срок действия которого истёк 57 секунд назад, прошёл проверку, а просроченный на 63 секунды — нет.

    Что записала проверка
    fact jwt-clock-skew = 60s
  • Обязательное утверждение о сроке действия

    no

    Токен без exp прошёл проверку; чтобы сделать это утверждение обязательным, укажите exp в check_claims.

    Что записала проверка
    fact jwt-exp-required = no
    evidence jwt-exp-required: verified

Использование

Подпишите токен ES256 с идентификатором ключа и проверьте его с суженным списком алгоритмов и обязательными iss и exp.
import json
import time

from jwcrypto import jwk, jwt

key = jwk.JWK.generate(kty="EC", crv="P-256", kid="2026-10")
public = jwk.JWK.from_json(key.export_public())

token = jwt.JWT(header={"alg": "ES256", "kid": "2026-10"}, claims={"sub": "user-42", "iss": "https://auth.example.com", "exp": int(time.time()) + 600})
token.make_signed_token(key)

# algs narrows the default list; check_claims makes iss and exp mandatory.
checked = jwt.JWT(
    jwt=token.serialize(),
    key=public,
    algs=["ES256"],
    check_claims={"iss": "https://auth.example.com", "exp": None},
)
print("sub:", json.loads(checked.claims)["sub"])

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера python:3.14.8-slim-trixie, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
pip install --quiet --root-user-action=ignore jwcrypto==1.6.1 cryptography==50.0.2
Команда
python usage.py
Вывод должен совпадать (регулярное выражение)
sub: user-42

Библиотеки