Saltar al contenido
ecdsa.com
C · OpenSSL · Línea de comandos

OpenSSL

OpenSSL, la biblioteca en C y herramienta de línea de comandos que sostiene la mayor parte del TLS de internet y buena parte de sus firmas: firmas DER, nonces aleatorios con RFC 6979 a petición, todas las curvas habituales, y ML-KEM, ML-DSA y SLH-DSA desde la 3.5.
Licencia
Apache-2.0
Versión comprobada
3.5.9 ·
Código fuente
github.com/openssl/openssl
Documentación
docs.openssl.org/3.5/

Compatibilidad de un vistazo

ECDSAEdDSACriptografía poscuántica

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

OpenSSL proporciona libcrypto y libssl para C y, a través de enlaces (bindings), para Node.js, Python, PHP y Ruby, además del comando openssl, que es lo primero con lo que se encuentra la mayoría. Los datos de esta página se comprobaron con el paquete de la distribución Alpine 3.24.2, OpenSSL 3.5.9 (la rama 3.5 de soporte a largo plazo); las versiones más recientes del proyecto en ese momento eran la 3.6.5 y la 4.0.3, así que el comportamiento puede diferir en ellas.

Las firmas ECDSA de EVP_DigestSign y de openssl dgst -sign son DER ASN.1, de 70 a 72 bytes en P-256. Los nonces son aleatorios salvo que establezcas el parámetro nonce-type en 1 (OpenSSL 3.2+), lo que reprodujo el vector de prueba P-256 del RFC 6979; en la línea de comandos ese parámetro solo es accesible mediante pkeyutl, como muestra el ejemplo. s no se normaliza y la verificación acepta high-S. EVP_PKEY_sign firma un resumen que calculas tú.

Una entrada mal formada es un error, no un resultado falso: EVP_DigestVerify devolvió -1 con una firma r‖s en bruto de 64 bytes, y la CLI lo indicó así. openssl dgst -verify imprimió Verified OK para una firma correcta, Verification failure para una firma válida sobre otros datos y Error verifying data para 64 bytes que no son DER, así que en la 3.5.9 un problema de formato se distingue de una falsificación por el texto del mensaje. Convierte las firmas en bruto con el conversor DER ⇄ en bruto, o deja que el constructor de comandos OpenSSL componga la invocación correcta.

Las curvas incluyen de P-224 a P-521, secp256k1 y las curvas Brainpool r1, además de Ed25519 y Ed448. Desde la 3.5, el proveedor predeterminado también implementa ML-KEM, ML-DSA y SLH-DSA, y el grupo TLS híbrido X25519MLKEM768. Las claves privadas llegan como PKCS#8 (sin cifrar o cifrado) o SEC1, y las claves públicas como SPKI o dentro de un certificado: genpkey escribe -----BEGIN PRIVATE KEY-----, y el heredado openssl ec escribe -----BEGIN EC PRIVATE KEY-----; un archivo con la etiqueta EC siempre es una clave privada.

La mayoría de los mensajes de error que la gente busca provienen de la carga de claves —no start line, header too long, unsupported del decodificador— y se explican en las páginas de errores enlazadas.

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/openssl/run.sh ejecutó OpenSSL 3.5.9 en la imagen de contenedor alpine:3.24.2 el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Codificación de la firma ECDSA

    der

    EVP_DigestSign y openssl dgst -sign producen DER ASN.1.

    Registrado por la comprobación
    fact ecdsa-signature-encoding = der
  • Nonce de ECDSA

    rfc6979-opt-in

    Aleatorio por defecto; el parámetro de firma nonce-type establecido en 1 reprodujo el apéndice A.2.5 del RFC 6979.

    Registrado por la comprobación
    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 al firmar

    either

    s no se normaliza: aparecieron valores high-S entre 64 firmas.

    Registrado por la comprobación
    fact ecdsa-low-s-sign = either
  • High-S al verificar

    accepts-high-s

    Se verificó una firma DER con s sustituido por n − s.

    Registrado por la comprobación
    fact ecdsa-low-s-verify = accepts-high-s
  • De qué calcula el hash la llamada de firma

    hashes-message-or-digest

    EVP_DigestSign calcula el hash de los datos; EVP_PKEY_sign firma un resumen precalculado, y esa firma se verificó contra el mensaje.

    Registrado por la comprobación
    fact ecdsa-prehash = hashes-message-or-digest
  • Firma ECDSA mal formada

    returns-error

    EVP_DigestVerify devolvió -1 (un error, no 0) con una firma r‖s en bruto y con un blob de 3 bytes.

    Registrado por la comprobación
    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)
  • Salida de la verificación en caso de fallo

    distinguishable

    openssl dgst -verify imprimió Error verifying data para una firma que no es DER y Verification failure para una firma válida sobre otros datos.

    Registrado por la comprobación
    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

    Con P-224, P-256, P-384, P-521, secp256k1 y brainpoolP256r1/P384r1/P512r1 se firmó y se verificó.

    Registrado por la comprobación
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 y Ed448.

    Registrado por la comprobación
    fact eddsa = Ed25519, Ed448
  • Algoritmos poscuánticos

    ML-KEMML-DSASLH-DSA

    En el proveedor predeterminado, con ML-KEM-768 se encapsuló y desencapsuló, y con ML-DSA-65 y SLH-DSA-SHA2-128s se firmó y se verificó.

    Registrado por la comprobación
    fact pqc = ML-KEM, ML-DSA, SLH-DSA
    evidence pqc: TLS groups with ML-KEM: MLKEM512 MLKEM768 MLKEM1024 SecP256r1MLKEM768 X25519MLKEM768 SecP384r1MLKEM1024 
  • Formatos de clave pública

    spki-pemspki-derx509

    La misma firma se verificó con SPKI PEM y DER y con la clave pública de un certificado X.509, y también con un SPKI que contiene un punto comprimido.

    Registrado por la comprobación
    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 clave privada

    pkcs8-pempkcs8-derencrypted-pkcs8sec1-pemsec1-der

    Con PKCS#8 PEM y DER, PKCS#8 cifrado y SEC1 PEM y DER se firmó en todos los casos; genpkey escribe PKCS#8 y openssl ec, SEC1.

    Registrado por la comprobación
    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

Genera una clave P-256, firma y verifica con dgst, firma de forma determinista mediante pkeyutl y crea una firma 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"

El arnés de pruebas de fragmentos de código ejecuta cada ejemplo tal cual en la imagen de contenedor alpine:3.24.2, y la ejecución falla si la salida no coincide.

Configuración
apk add --no-cache -q openssl=3.5.9-r0 >/dev/null
Comando
sh usage.sh
La salida debe coincidir (expresión regular)
Verified OK\ndeterministic: identical signatures\nML-DSA-65 signature: 3309 bytes

Bibliotecas