Pular para o conteúdo
ecdsa.com
Python

JWCrypto

O JWCrypto, da Red Hat, a implementação JOSE usada pelo FreeIPA e por muitos serviços Python corporativos: cobertura completa de JWS/JWE/JWK, incluindo ML-DSA, tolerância padrão de 60 segundos e alg: none só quando solicitado.
Licença
LGPL-3.0-or-later
Versão verificada
1.6.1 ·
Código-fonte
github.com/latchset/jwcrypto
Documentação
jwcrypto.readthedocs.io/en/latest/

Visão geral da compatibilidade

JOSE e JWT

Algoritmos JWS
HS256HS384HS512RS256RS384RS512PS256PS384PS512ES256ES384ES512ES256KEdDSAEd25519Ed448ML-DSA-44ML-DSA-65ML-DSA-87

O JWCrypto implementa a família JOSE sobre o pyca/cryptography: JWK e conjuntos de chaves JWK, JWS, JWE e JWT. Tudo se baseia em objetos de chave — um jwk.JWK criado a partir de PEM, de JSON ou de generate() —, o que torna o tipo da chave explícito em cada chamada. A licença é LGPL-3.0-or-later, o que importa se você redistribuir cópias modificadas.

Sua cobertura de algoritmos é a mais ampla entre as bibliotecas JOSE para Python: as famílias HS, RS, PS e ES, ES256K, EdDSA e os totalmente especificados Ed25519 e Ed448 e, na versão 1.6.1, também ML-DSA-44, ML-DSA-65 e ML-DSA-87 com chaves AKP do rascunho de JOSE pós-quântico — todos funcionaram na assinatura e na verificação no teste, e todos estão na lista padrão de algoritmos permitidos. É nessa lista padrão que está a armadilha: a menos que você passe algs, qualquer um deles é aceito para uma chave compatível; por isso, restrinja a lista ao que seu emissor realmente usa, como faz o exemplo.

Tokens sem assinatura são recusados com Algorithm not allowed, a menos que none esteja na lista, e um token HS256 apresentado com uma JWK pública EC falha, porque esse tipo de chave não consegue executar HMAC. O ES256 recusa uma chave P-384 com Curve requested is "P-256", but key curve is "P-384". As assinaturas ES256 estão na forma r‖s bruta de 64 bytes; uma assinatura DER falha com Verification failed for all signatures — confira a codificação no depurador de JWT.

Dois padrões diferem dos de outras bibliotecas. A verificação da expiração tolera 60 segundos de diferença de relógio: um token que tinha expirado 57 segundos antes ainda foi considerado válido, e um que tinha expirado 63 segundos antes, não — mais tolerante que o PyJWT ou o jose, que não admitem nenhuma. Além disso, exp é opcional: um token sem ela foi considerado válido, a menos que check_claims a inclua.

Monte conjuntos de chaves com o construtor de JWKS e inspecione tokens criptografados com o inspetor de JWE; para chaves e assinaturas ML-DSA, o playground de assinaturas pós-quânticas mostra os tamanhos envolvidos.

Os fatos e as verificações por trás deles

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/jwcrypto/run.sh executou JWCrypto 1.6.1 em cryptography 50.0.2 na imagem de contêiner python:3.14.8-slim-trixie em 20 de outubro de 2026; a página precisa corresponder ao que a verificação imprimiu, e cada fato lista essas linhas.

  • Algoritmos JWS

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

    Assinatura e verificação funcionaram com HS, RS, PS e ES em 256/384/512 bits, ES256K, EdDSA, Ed25519, Ed448 e ML-DSA-44/65/87 (com chaves AKP); todos estão na lista padrão de algoritmos permitidos.

    Registrado pela verificação
    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
  • Codificação da assinatura ES256

    raw

    As assinaturas ES256 são 64 bytes de r‖s brutos.

    Registrado pela verificação
    fact jws-ecdsa-encoding = raw
  • Assinatura DER dentro de um JWS

    rejected

    Uma assinatura ES256 codificada em DER falha com InvalidJWSSignature: Verification failed for all signatures.

    Registrado pela verificação
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • Lista de algoritmos permitidos

    from-key

    Sem algs, vale a lista padrão e quem decide é o tipo da chave: um token ES256 foi verificado com uma chave EC, e um token RS256 verificado com essa mesma chave falhou.

    Registrado pela verificação
    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')]"]
  • Tokens sem assinatura (alg none)

    opt-in

    Por padrão, um token sem assinatura falha com Algorithm not allowed e só é verificado com algs=["none"].

    Registrado pela verificação
    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
  • Chave pública usada como segredo HMAC

    rejected

    Um token HS256 verificado com a JWK pública EC falhou; só uma JWK oct criada de propósito com o texto PEM o verificou.

    Registrado pela verificação
    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
  • Curva conferida com o alg

    enforced

    Assinar ES256 com uma chave P-384 lança Curve requested is "P-256", but key curve is "P-384".

    Registrado pela verificação
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidJWKValue: Curve requested is "P-256", but key curve is "P-384"
  • Tolerância padrão de diferença de relógio

    60s

    Por padrão, a expiração tolera 60 segundos de diferença de relógio: um token expirado 57 segundos antes foi considerado válido; um expirado 63 segundos antes, não.

    Registrado pela verificação
    fact jwt-clock-skew = 60s
  • Claim de expiração obrigatória

    no

    Um token sem exp foi considerado válido; inclua exp em check_claims para exigi-la.

    Registrado pela verificação
    fact jwt-exp-required = no
    evidence jwt-exp-required: verified

Uso

Assine um token ES256 com um ID de chave e verifique-o com a lista de algoritmos restrita e iss e exp obrigatórias.
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"])

O executor de trechos de código roda cada exemplo literalmente na imagem de contêiner python:3.14.8-slim-trixie, e a execução falha se a saída não corresponder.

Configuração
pip install --quiet --root-user-action=ignore jwcrypto==1.6.1 cryptography==50.0.2
Comando
python usage.py
A saída deve corresponder a (expressão regular)
sub: user-42

Bibliotecas