Pular para o conteúdo
ecdsa.com
Python

pyca/cryptography

O pacote cryptography da Python Cryptographic Authority, a biblioteca criptográfica padrão para Python: ECDSA sobre o OpenSSL com assinaturas DER e RFC 6979 opcional, EdDSA, ML-KEM e ML-DSA, e nenhum suporte a JWK.
Licença
Apache-2.0 OR BSD-3-Clause
Versão verificada
50.0.2 ·
Código-fonte
github.com/pyca/cryptography
Documentação
cryptography.io/en/latest/

Visão geral da compatibilidade

ECDSAEdDSAPós-quântica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Formatos de chave pública
spki-pemspki-dersec1-uncompressedsec1-compressedopenssh
Formatos de chave privada
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-derraw-scalaropenssh

O cryptography é o que a maior parte do código Python — PyJWT, Authlib, paramiko, certbot, os SDKs da AWS e do Google — usa para a criptografia de verdade. Sua camada hazmat expõe chaves e assinaturas diretamente; os wheels trazem o próprio OpenSSL, então o comportamento segue a versão do pacote, e não o sistema operacional (a 50.0.2 vem com o OpenSSL 4.0.3).

As assinaturas ECDSA saem em ASN.1 DER, e verify() espera DER. O WebCrypto, o JWS e a maioria dos tokens de hardware usam a forma r‖s bruta de 64 bytes, que verify() rejeita com o mesmo InvalidSignature de uma assinatura forjada. decode_dss_signature e encode_dss_signature convertem entre DER e o par de inteiros; o padding de largura fixa fica por sua conta, exatamente como mostra o exemplo, ou use o conversor DER ⇄ bruto para conferir manualmente.

O nonce é aleatório, a menos que você peça a RFC 6979: ec.ECDSA(hashes.SHA256(), deterministic_signing=True) reproduziu exatamente o vetor de teste P-256 da RFC — inclusive o s high-S do vetor, porque nada no pacote normaliza o s, e a verificação aceita as duas metades. Assinaturas destinadas a um verificador low-S, como a libsecp256k1 ou a @noble/curves, precisam antes ter o s substituído por n − s. sign() e verify() calculam o hash dos dados por conta própria; passe Prehashed(hashes.SHA256()) quando já tiver um digest, senão o hash será calculado duas vezes.

A cobertura de curvas é ampla — da P-224 à P-521, a secp256k1 e as curvas Brainpool r1 —, além de Ed25519, Ed448, ML-KEM-768/1024 e ML-DSA-44/65/87 na 50.0.2. As chaves são carregadas de PEM e DER (SPKI, PKCS#8, PKCS#8 criptografado, SEC1), OpenSSH e pontos SEC1 isolados; não há leitor de JWK, então JWKs passam pelo PyJWT, pelo Authlib ou pelo jwcrypto, ou pelo conversor de JWK.

A maioria dos erros que as pessoas pesquisam vem do carregamento de chaves, e não da assinatura: uma chave pública passada a load_pem_private_key, uma chave criptografada sem senha ou um ponto que não está na curva indicada. O verificador de assinaturas e a matriz de interoperabilidade de bibliotecas ajudam quando uma assinatura vinda de outra stack não passa na verificação.

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

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/pyca-cryptography/run.sh executou pyca/cryptography 50.0.2 em OpenSSL 4.0.3 29 Sep 2026 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.

  • Codificação da assinatura ECDSA

    der

    sign() retorna ASN.1 DER e verify() espera DER.

    Registrado pela verificação
    fact ecdsa-signature-encoding = der
  • Low-S ao assinar

    either

    O s não é normalizado: valores high-S apareceram entre 64 assinaturas.

    Registrado pela verificação
    fact ecdsa-low-s-sign = either
  • High-S ao verificar

    accepts-high-s

    Uma assinatura com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Nonce do ECDSA

    rfc6979-opt-in

    Aleatório por padrão; deterministic_signing=True reproduziu exatamente o apêndice A.2.5 da RFC 6979, inclusive o s high-S do vetor.

    Registrado pela verificação
    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
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    sign() calcula o hash dos dados; com Prehashed(hashes.SHA256()), assina um digest calculado por você, e essa assinatura foi verificada contra a mensagem original.

    Registrado pela verificação
    fact ecdsa-prehash = hashes-message-or-digest
  • Assinatura ECDSA malformada

    throws

    Uma assinatura r‖s bruta, um blob de 3 bytes e uma assinatura sobre outra mensagem lançam, todos, InvalidSignature; a exceção não diz qual é o caso.

    Registrado pela verificação
    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
  • Curvas ECDSA

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

    Assinatura e verificação funcionaram com P-224, P-256, P-384, P-521, secp256k1 e as curvas Brainpool r1.

    Registrado pela verificação
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 e Ed448.

    Registrado pela verificação
    fact eddsa = Ed25519, Ed448
  • Algoritmos pós-quânticos

    ML-KEMML-DSA

    O ML-KEM-768 e o ML-KEM-1024 encapsularam e desencapsularam, e o ML-DSA-44/65/87 assinou e verificou; não há classe ML-KEM-512 nem SLH-DSA.

    Registrado pela verificação
    fact pqc = ML-KEM, ML-DSA
    evidence pqc: ML-KEM-512 class present: False; SLH-DSA module present: False
  • Formatos de chave pública

    spki-pemspki-dersec1-uncompressedsec1-compressedopenssh

    As chaves públicas foram carregadas de SPKI PEM e DER, OpenSSH e pontos SEC1 (comprimidos ou não comprimidos) por meio de from_encoded_point; um x‖y de 64 bytes sem o prefixo foi rejeitado, e não há suporte a JWK.

    Registrado pela verificação
    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
  • Formatos de chave privada

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

    As chaves privadas foram carregadas de PKCS#8 PEM e DER, PKCS#8 criptografado, SEC1 PEM e DER, OpenSSH e de um escalar bruto por meio de derive_private_key.

    Registrado pela verificação
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der, raw-scalar, openssh

Uso

Assine com P-256, converta a assinatura DER para r‖s bruto e de volta, e verifique.
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")

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 cryptography==50.0.2
Comando
python usage.py
A saída deve corresponder a (expressão regular)
verified: True, raw bytes: 64

Ferramentas

Termos

Bibliotecas

Artigos

Receitas

Erros

Bibliotecas