Aller au contenu
ecdsa.com
Python

pyca/cryptography

Le paquet cryptography de la Python Cryptographic Authority, la bibliothèque cryptographique standard de Python : ECDSA adossé à OpenSSL avec signatures DER et RFC 6979 en option, EdDSA, ML-KEM et ML-DSA, sans prise en charge de JWK.
Licence
Apache-2.0 OR BSD-3-Clause
Version vérifiée
50.0.2 ·
Code source
github.com/pyca/cryptography
Documentation
cryptography.io/en/latest/

Compatibilité en un coup d’œil

ECDSAEdDSAPost-quantique

Courbes ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formats de clé publique
spki-pemspki-dersec1-uncompressedsec1-compressedopenssh
Formats de clé privée
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalaropenssh

cryptography est ce qu’utilise la plupart du code Python (PyJWT, Authlib, paramiko, certbot, les SDK AWS et Google) pour la cryptographie proprement dite. Sa couche hazmat expose directement les clés et les signatures ; les wheels embarquent leur propre OpenSSL, si bien que le comportement dépend de la version du paquet et non du système d’exploitation (la 50.0.2 embarque OpenSSL 4.0.3).

Les signatures ECDSA sortent en ASN.1 DER, et verify() attend du DER. WebCrypto, JWS et la plupart des jetons matériels utilisent la forme r‖s brute de 64 octets, que verify() rejette avec la même exception InvalidSignature qu’une signature falsifiée. decode_dss_signature et encode_dss_signature convertissent entre le DER et la paire d’entiers ; le remplissage à largeur fixe est à votre charge, exactement comme le montre l’exemple, ou utilisez le convertisseur DER ⇄ brut pour vérifier à la main.

Le nonce est aléatoire, sauf si vous demandez la RFC 6979 : ec.ECDSA(hashes.SHA256(), deterministic_signing=True) a reproduit exactement le vecteur de test P-256 de la RFC, y compris sa valeur de s élevée (high-S), car rien dans le paquet ne normalise s, et la vérification accepte les deux moitiés. Les signatures destinées à un vérificateur low-S comme libsecp256k1 ou @noble/curves doivent d’abord voir s remplacé par n − s. sign() et verify() hachent eux-mêmes les données ; passez Prehashed(hashes.SHA256()) si vous disposez déjà d’un condensat, sinon vous hacherez deux fois.

La couverture des courbes est large (de P-224 à P-521, secp256k1 et les courbes Brainpool r1), avec en plus Ed25519, Ed448, ML-KEM-768/1024 et ML-DSA-44/65/87 dans la 50.0.2. Les clés se chargent depuis PEM et DER (SPKI, PKCS#8, PKCS#8 chiffré, SEC1), OpenSSH et des points SEC1 bruts ; il n’existe pas de lecteur JWK, si bien que les JWK passent par PyJWT, Authlib ou jwcrypto, ou par le convertisseur JWK.

La plupart des erreurs que les développeurs recherchent proviennent du chargement des clés et non de la signature : une clé publique passée à load_pem_private_key, une clé chiffrée sans mot de passe ou un point qui n’est pas sur la courbe nommée. Le vérificateur de signatures et la matrice d’interopérabilité des bibliothèques vous aident quand une signature issue d’une autre pile ne passe pas la vérification.

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/pyca-cryptography/run.sh a exécuté pyca/cryptography 50.0.2 sur OpenSSL 4.0.3 29 Sep 2026 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.

  • Encodage des signatures ECDSA

    der

    sign() renvoie de l’ASN.1 DER et verify() attend du DER.

    Relevé par la vérification
    fact ecdsa-signature-encoding = der
  • Low-S à la signature

    either

    s n’est pas normalisé : des valeurs high-S sont apparues parmi 64 signatures.

    Relevé par la vérification
    fact ecdsa-low-s-sign = either
  • High-S à la vérification

    accepts-high-s

    Une signature dont s a été remplacé par n − s a été vérifiée.

    Relevé par la vérification
    fact ecdsa-low-s-verify = accepts-high-s
  • Nonce ECDSA

    rfc6979-opt-in

    Aléatoire par défaut ; deterministic_signing=True a reproduit exactement l’annexe A.2.5 de la RFC 6979, y compris sa valeur de s élevée (high-S).

    Relevé par la vérification
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: default repeats: False; deterministic_signing=True reproduces RFC 6979 A.2.5 (r and high s): True
  • Ce que l’appel de signature hache

    hashes-message-or-digest

    sign() hache les données ; avec Prehashed(hashes.SHA256()), la méthode signe un condensat calculé par vos soins, et cette signature a été vérifiée sur le message d’origine.

    Relevé par la vérification
    fact ecdsa-prehash = hashes-message-or-digest
  • Signature ECDSA mal formée

    throws

    Une signature r‖s brute, un bloc de 3 octets et une signature portant sur un autre message lèvent tous InvalidSignature ; l’exception ne précise pas lequel de ces cas s’est produit.

    Relevé par la vérification
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: raw r||s: InvalidSignature
    evidence ecdsa-malformed-signature: 3 bytes: InvalidSignature
    evidence ecdsa-malformed-signature: wrong message: InvalidSignature
  • Courbes ECDSA

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    Signature et vérification réussies avec P-224, P-256, P-384, P-521, secp256k1 et les courbes Brainpool r1.

    Relevé par la vérification
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 et Ed448.

    Relevé par la vérification
    fact eddsa = Ed25519, Ed448
  • Algorithmes post-quantiques

    ML-KEMML-DSA

    Encapsulation et décapsulation réussies avec ML-KEM-768 et ML-KEM-1024, signature et vérification avec ML-DSA-44/65/87 ; il n’y a ni classe ML-KEM-512 ni SLH-DSA.

    Relevé par la vérification
    fact pqc = ML-KEM, ML-DSA
    evidence pqc: ML-KEM-512 class present: False; SLH-DSA module present: False
  • Formats de clé publique

    spki-pemspki-dersec1-uncompressedsec1-compressedopenssh

    Clés publiques chargées depuis SPKI PEM et DER, OpenSSH et des points SEC1 (compressés ou non compressés) via from_encoded_point ; un x‖y de 64 octets sans le préfixe a été rejeté, et JWK n’est pas pris en charge.

    Relevé par la vérification
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, openssh
    evidence public-key-formats: no JWK reader or writer in the package
  • Formats de clé privée

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalaropenssh

    Clés privées chargées depuis PKCS#8 PEM et DER, PKCS#8 chiffré, SEC1 PEM et DER, OpenSSH, et depuis un scalaire brut via derive_private_key.

    Relevé par la vérification
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar, openssh

Utilisation

Signer avec P-256, convertir la signature DER en r‖s brut et inversement, puis vérifier.
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature

key = ec.generate_private_key(ec.SECP256R1())
message = b"order #1042 approved"

# DER out of sign(); turn it into the 64-byte r||s that WebCrypto and JOSE expect.
der = key.sign(message, ec.ECDSA(hashes.SHA256()))
r, s = decode_dss_signature(der)
raw = r.to_bytes(32, "big") + s.to_bytes(32, "big")

# A raw signature from WebCrypto goes the other way before verify().
back = encode_dss_signature(int.from_bytes(raw[:32], "big"), int.from_bytes(raw[32:], "big"))
try:
    key.public_key().verify(back, message, ec.ECDSA(hashes.SHA256()))
    print("verified: True, raw bytes:", len(raw))
except InvalidSignature:
    print("verified: False")

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 cryptography==50.0.2
Commande
python usage.py
La sortie doit correspondre à cette expression régulière
verified: True, raw bytes: 64

Outils

Termes

Bibliothèques

Articles

Recettes

Erreurs

Bibliothèques