Pular para o conteúdo
ecdsa.com
C · OpenSSL · Linha de comando

OpenSSL

O OpenSSL, a biblioteca C e ferramenta de linha de comando por trás da maior parte do TLS da internet e de boa parte das assinaturas digitais: assinaturas DER, nonces aleatórios com RFC 6979 sob demanda, todas as curvas comuns e ML-KEM, ML-DSA e SLH-DSA desde a 3.5.
Licença
Apache-2.0
Versão verificada
3.5.9 ·
Código-fonte
github.com/openssl/openssl
Documentação
docs.openssl.org/3.5/

Visão geral da compatibilidade

ECDSAEdDSAPós-quântica

Curvas ECDSA
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
Algoritmos pós-quânticos
ML-KEMML-DSASLH-DSA
Formatos de chave pública
spki-pemspki-derx509
Formatos de chave privada
pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-der

O OpenSSL fornece libcrypto e libssl para C e, por meio de bindings, para Node.js, Python, PHP e Ruby, além do comando openssl, que é o primeiro contato da maioria das pessoas com ele. Os fatos desta página foram verificados no pacote de distribuição do Alpine 3.24.2, OpenSSL 3.5.9 (a linha 3.5 de suporte de longo prazo); as versões mais recentes do projeto original na época eram a 3.6.5 e a 4.0.3, então o comportamento pode ser diferente nelas.

As assinaturas ECDSA de EVP_DigestSign e de openssl dgst -sign são ASN.1 DER — de 70 a 72 bytes na P-256. Os nonces são aleatórios, a menos que você defina o parâmetro nonce-type como 1 (OpenSSL 3.2+), o que reproduziu o vetor de teste P-256 da RFC 6979; na linha de comando, esse parâmetro só é acessível pelo pkeyutl, como mostra o exemplo. O s não é normalizado, e a verificação aceita high-S. EVP_PKEY_sign assina um digest calculado por você.

Uma entrada malformada é um erro, e não um resultado falso: EVP_DigestVerify retornou -1 para uma assinatura r‖s bruta de 64 bytes, e a CLI deixou isso claro. openssl dgst -verify imprimiu Verified OK para uma assinatura correta, Verification failure para uma assinatura válida sobre outros dados e Error verifying data para 64 bytes que não são DER — então, na 3.5.9, dá para distinguir um problema de formato de uma falsificação pela redação da mensagem. Converta assinaturas brutas com o conversor DER ⇄ bruto ou deixe o construtor de comandos OpenSSL montar a chamada certa.

As curvas incluem de P-224 a P-521, secp256k1 e as curvas Brainpool r1, além de Ed25519 e Ed448. Desde a 3.5, o provedor padrão também implementa ML-KEM, ML-DSA e SLH-DSA e o grupo híbrido de TLS X25519MLKEM768. As chaves vêm como chaves privadas PKCS#8 (simples ou criptografadas) ou SEC1 e como chaves públicas SPKI ou de certificado: genpkey grava -----BEGIN PRIVATE KEY-----, e o comando legado openssl ec grava -----BEGIN EC PRIVATE KEY----- — um arquivo com o rótulo EC é sempre uma chave privada.

A maioria das mensagens de erro que as pessoas pesquisam vem do carregamento de chaves — no start line, header too long, unsupported do decodificador — e está explicada nas páginas de erro vinculadas.

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

Nada abaixo foi copiado da documentação. A verificação content/libraries/verify/openssl/run.sh executou OpenSSL 3.5.9 na imagem de contêiner alpine:3.24.2 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

    EVP_DigestSign e openssl dgst -sign produzem ASN.1 DER.

    Registrado pela verificação
    fact ecdsa-signature-encoding = der
  • Nonce do ECDSA

    rfc6979-opt-in

    Aleatório por padrão; o parâmetro de assinatura nonce-type definido como 1 reproduziu o apêndice A.2.5 da RFC 6979.

    Registrado pela verificação
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: default signatures repeat: no; nonce-type 1 (deterministic) reproduces the RFC 6979 A.2.5 r: yes
  • 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 DER com s substituído por n − s foi verificada.

    Registrado pela verificação
    fact ecdsa-low-s-verify = accepts-high-s
  • Entrada do hash na chamada de assinatura

    hashes-message-or-digest

    EVP_DigestSign calcula o hash dos dados; EVP_PKEY_sign assina um digest pré-calculado, e essa assinatura foi verificada contra a mensagem.

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

    returns-error

    EVP_DigestVerify retornou -1 (um erro, e não 0) para uma assinatura r‖s bruta e para um blob de 3 bytes.

    Registrado pela verificação
    fact ecdsa-malformed-signature = returns-error
    evidence ecdsa-malformed-signature: EVP_DigestVerify returned -1 for raw r||s and -1 for 3 bytes (1 = valid, 0 = invalid, negative = error)
  • Saída da verificação em caso de falha

    distinguishable

    openssl dgst -verify imprimiu Error verifying data para uma assinatura que não está em DER e Verification failure para uma assinatura válida sobre outros dados.

    Registrado pela verificação
    fact cli-verify-output = distinguishable
    evidence cli-verify-output: valid: Verified OK; 64 zero bytes: Error verifying data; valid signature over other data: Verification failure
  • Curvas ECDSA

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

    Assinatura e verificação funcionaram com P-224, P-256, P-384, P-521, secp256k1 e brainpoolP256r1/P384r1/P512r1.

    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-DSASLH-DSA

    No provedor padrão, o ML-KEM-768 encapsulou e desencapsulou, e o ML-DSA-65 e o SLH-DSA-SHA2-128s assinaram e verificaram.

    Registrado pela verificação
    fact pqc = ML-KEM, ML-DSA, SLH-DSA
    evidence pqc: TLS groups with ML-KEM: MLKEM512 MLKEM768 MLKEM1024 SecP256r1MLKEM768 X25519MLKEM768 SecP384r1MLKEM1024 
  • Formatos de chave pública

    spki-pemspki-derx509

    SPKI em PEM e DER e a chave pública de um certificado X.509 verificaram a mesma assinatura; um SPKI com um ponto comprimido também passou na verificação.

    Registrado pela verificação
    fact public-key-formats = spki-pem, spki-der, x509
    evidence public-key-formats: SPKI carrying a compressed point (openssl ec -conv_form compressed) verifies: yes
  • Formatos de chave privada

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-der

    A assinatura funcionou com PKCS#8 em PEM e DER, PKCS#8 criptografado e SEC1 em PEM e DER; genpkey grava PKCS#8, e openssl ec grava SEC1.

    Registrado pela verificação
    fact private-key-formats = pkcs8-pem, pkcs8-der, encrypted-pkcs8, sec1-pem, sec1-der
    evidence private-key-formats: genpkey writes -----BEGIN PRIVATE KEY-----; openssl ec writes -----BEGIN EC PRIVATE KEY-----

Uso

Gere uma chave P-256, assine e verifique com dgst, assine de forma determinística pelo pkeyutl e crie uma assinatura ML-DSA-65.
# P-256 key as PKCS#8, public key as SPKI.
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pem
openssl pkey -in key.pem -pubout -out pub.pem

printf 'order #1042 approved' > msg.txt
openssl dgst -sha256 -sign key.pem -out sig.der msg.txt   # ASN.1 DER, 70-72 bytes
openssl dgst -sha256 -verify pub.pem -signature sig.der msg.txt

# Deterministic RFC 6979 signing (OpenSSL 3.2+) goes through pkeyutl.
openssl pkeyutl -sign -inkey key.pem -rawin -digest sha256 -pkeyopt nonce-type:1 -in msg.txt -out det1.der
openssl pkeyutl -sign -inkey key.pem -rawin -digest sha256 -pkeyopt nonce-type:1 -in msg.txt -out det2.der
cmp -s det1.der det2.der && echo "deterministic: identical signatures"

# Post-quantum: ML-DSA-65 key and signature.
openssl genpkey -algorithm ML-DSA-65 -out mldsa.pem
openssl pkeyutl -sign -inkey mldsa.pem -rawin -in msg.txt -out mldsa.sig
echo "ML-DSA-65 signature: $(wc -c < mldsa.sig) bytes"

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

Configuração
apk add --no-cache -q openssl=3.5.9-r0 >/dev/null
Comando
sh usage.sh
A saída deve corresponder a (expressão regular)
Verified OK\ndeterministic: identical signatures\nML-DSA-65 signature: 3309 bytes

Bibliotecas