Aller au contenu
ecdsa.com
Python

JWCrypto

JWCrypto de Red Hat, l’implémentation JOSE qu’utilisent FreeIPA et de nombreux services Python d’entreprise : couverture complète de JWS, JWE et JWK, ML-DSA compris, une marge de tolérance de 60 secondes par défaut, et alg: none uniquement sur demande.
Licence
LGPL-3.0-or-later
Version vérifiée
1.6.1 ·
Code source
github.com/latchset/jwcrypto
Documentation
jwcrypto.readthedocs.io/en/latest/

Compatibilité en un coup d’œil

JOSE et JWT

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

JWCrypto implémente la famille JOSE en s’appuyant sur pyca/cryptography : JWK et jeux de clés JWK, JWS, JWE et JWT. Tout repose sur des objets clés (un jwk.JWK construit à partir de PEM, de JSON ou de generate()), ce qui rend le type de clé explicite à chaque appel. La bibliothèque est sous licence LGPL-3.0-or-later, ce qui compte si vous redistribuez des copies modifiées.

Sa couverture d’algorithmes est la plus large des bibliothèques JOSE Python : les familles HS, RS, PS et ES, ES256K, EdDSA et les algorithmes entièrement spécifiés Ed25519 et Ed448, ainsi que, dans la 1.6.1, ML-DSA-44, ML-DSA-65 et ML-DSA-87 avec les clés AKP de l’Internet-Draft JOSE post-quantique, tous signés et vérifiés avec succès lors des essais et tous présents dans la liste autorisée par défaut. C’est justement cette liste par défaut qui pose problème : sauf si vous passez algs, n’importe lequel d’entre eux est accepté pour une clé compatible ; restreignez-la donc à ce que votre émetteur utilise réellement, comme le fait l’exemple.

Les jetons non signés sont refusés avec Algorithm not allowed, sauf si none est listé, et un jeton HS256 présenté avec une JWK publique EC échoue, car ce type de clé ne peut pas exécuter HMAC. ES256 refuse une clé P-384 avec Curve requested is "P-256", but key curve is "P-384". Les signatures ES256 sont sous la forme r‖s brute de 64 octets ; une signature DER échoue avec Verification failed for all signatures : vérifiez l’encodage dans le débogueur JWT.

Deux valeurs par défaut diffèrent de celles des autres bibliothèques. Le contrôle de l’expiration tolère 60 secondes de décalage d’horloge : un jeton expiré depuis 57 secondes a encore été validé, un jeton expiré depuis 63 secondes ne l’a pas été, ce qui est plus permissif que PyJWT ou jose, qui n’en tolèrent aucun. Et exp est facultatif : un jeton sans ce claim a été validé, sauf si check_claims le mentionne.

Construisez des jeux de clés avec le générateur de JWKS et examinez les jetons chiffrés avec l’inspecteur JWE ; pour les clés et signatures ML-DSA, le bac à sable des signatures post-quantiques montre les tailles en jeu.

Les faits et les vérifications qui les fondent

Rien de ce qui suit n’est copié d’une documentation. La vérification content/libraries/verify/jwcrypto/run.sh a exécuté JWCrypto 1.6.1 sur cryptography 50.0.2 dans l’image de conteneur python:3.14.8-slim-trixie le 20 octobre 2026 ; la page doit correspondre à ce que la vérification a affiché, et chaque fait cite les lignes en question.

  • Algorithmes JWS

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

    Signature et vérification réussies avec HS, RS, PS et ES en 256/384/512 bits, ES256K, EdDSA, Ed25519, Ed448 et ML-DSA-44/65/87 (avec des clés AKP) ; tous figurent dans la liste autorisée par défaut.

    Relevé par la vérification
    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
  • Encodage des signatures ES256

    raw

    Les signatures ES256 font 64 octets, en r‖s brut.

    Relevé par la vérification
    fact jws-ecdsa-encoding = raw
  • Signature DER dans un JWS

    rejected

    Une signature ES256 encodée en DER échoue avec InvalidJWSSignature: Verification failed for all signatures.

    Relevé par la vérification
    fact jws-der-signature = rejected
    evidence jws-der-signature: InvalidJWSSignature: Verification failed for all signatures["Failed: [InvalidJWSSignature('Verification failed')]"]
  • Liste d’algorithmes autorisés

    from-key

    Sans algs, la liste par défaut s’applique et c’est le type de clé qui décide : un jeton ES256 a été vérifié avec une clé EC, et un jeton RS256 présenté à cette même clé a échoué.

    Relevé par la vérification
    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')]"]
  • Jetons non signés (alg none)

    opt-in

    Par défaut, un jeton non signé échoue avec Algorithm not allowed, et il n’est vérifié qu’avec algs=["none"].

    Relevé par la vérification
    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
  • Clé publique utilisée comme secret HMAC

    rejected

    Un jeton HS256 contrôlé face à la JWK publique EC a échoué ; seule une JWK oct construite délibérément avec le texte PEM l’a vérifié.

    Relevé par la vérification
    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
  • Courbe vérifiée par rapport à alg

    enforced

    Signer en ES256 avec une clé P-384 lève Curve requested is "P-256", but key curve is "P-384".

    Relevé par la vérification
    fact jwt-ecdsa-curve-check = enforced
    evidence jwt-ecdsa-curve-check: InvalidJWKValue: Curve requested is "P-256", but key curve is "P-384"
  • Décalage d’horloge toléré par défaut

    60s

    Par défaut, l’expiration tolère 60 secondes de décalage : un jeton expiré depuis 57 secondes a été validé, un jeton expiré depuis 63 secondes ne l’a pas été.

    Relevé par la vérification
    fact jwt-clock-skew = 60s
  • Claim d’expiration obligatoire

    no

    Un jeton sans exp a été validé ; listez exp dans check_claims pour le rendre obligatoire.

    Relevé par la vérification
    fact jwt-exp-required = no
    evidence jwt-exp-required: verified

Utilisation

Signer un jeton ES256 avec un identifiant de clé et le vérifier avec une liste d’algorithmes restreinte, iss et exp étant obligatoires.
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"])

Le banc de test des extraits de code exécute chaque exemple tel quel dans l’image de conteneur python:3.14.8-slim-trixie, et l’exécution échoue si la sortie ne correspond pas.

Mise en place
pip install --quiet --root-user-action=ignore jwcrypto==1.6.1 cryptography==50.0.2
Commande
python usage.py
La sortie doit correspondre à cette expression régulière
sub: user-42

Bibliothèques