Saltar al contenido
ecdsa.com
Node.js

node-forge

node-forge de Digital Bazaar, un conjunto de herramientas de TLS, PKI y PKCS en JavaScript puro: RSA, PKCS#12, X.509 y Ed25519, pero nada de ECDSA, así que no puede leer una clave ni un certificado de curva elíptica.
Licencia
BSD-3-Clause OR GPL-2.0
Versión comprobada
1.4.0 ·
Código fuente
github.com/digitalbazaar/forge
Documentación
github.com/digitalbazaar/forge#readme

Compatibilidad de un vistazo

EdDSA

EdDSA
Ed25519

node-forge nació como una implementación de TLS en JavaScript y hoy se usa sobre todo por sus componentes de PKI: generar y analizar certificados X.509 y CSR, leer y escribir contenedores PKCS#12, cifrado y firmas RSA, y un analizador ASN.1. Funciona igual en navegadores y en Node.js y no tiene código nativo.

No admite curvas elípticas aparte de Ed25519. forge.pki expone solo rsa y ed25519, y la comprobación confirmó la consecuencia: certificateFromPem() con un certificado ECDSA lanza Cannot read public key. OID is not RSA., y publicKeyFromPem() con una clave pública EC lanza Cannot read public key. Unknown OID., aunque los mismos bytes se analizan sin problema con el lector ASN.1 del propio forge. El código que inspecciona certificados del mundo real se encuentra con claves ECDSA constantemente y necesita otro analizador para ellas; X509Certificate de node:crypto o el decodificador de certificados admiten ambos tipos de clave.

Con RSA interopera sin problemas una vez que los parámetros coinciden. Las firmas PKCS#1 v1.5 y PSS hechas con forge se verificaron con node:crypto; PSS exige que el resumen, el resumen de MGF1 y la longitud de la sal coincidan en ambos lados, y forge te obliga a indicar los tres, como hace el ejemplo. Las claves públicas se cargan desde PEM de SPKI, de PKCS#1 y de certificado, y las claves privadas desde PKCS#8, PKCS#8 cifrado, PKCS#1 y PKCS#12; abre los contenedores en el inspector de PFX para ver qué contienen.

Las firmas Ed25519 de forge fueron idénticas byte a byte a las de node:crypto con la misma semilla. Su clave privada es la forma expandida de 64 bytes —la semilla seguida de la clave pública—, así que las bibliotecas que esperan la semilla de 32 bytes solo necesitan la primera mitad.

La verificación de firmas tiene un historial de análisis demasiado permisivo, y un caso sigue abierto. El aviso GHSA-86w9-cpqp-85rv (CVE-2026-85393), publicado en septiembre de 2026 sin versión corregida, indica que la verificación RSA PKCS#1 v1.5 en las versiones hasta la 1.4.0 ignora los elementos adicionales dentro del identificador de algoritmo de DigestInfo, lo que permite falsificar firmas con claves de exponente público pequeño, como 3. La comprobación reprodujo esa permisividad: un bloque con el relleno correcto y un elemento adicional en ese lugar se verificó en forge y node:crypto lo rechazó. Hasta que se publique una corrección, verifica las firmas PKCS#1 v1.5 con node:crypto o WebCrypto, y evita las claves RSA con exponente 3. La propia 1.4.0 corrigió la tanda anterior —se rechazaron tanto un campo adicional después del resumen como las firmas Ed25519 no canónicas—, y las versiones anteriores a la 1.3.0 tenían errores de falsificación de firmas previos (CVE-2022-24771, CVE-2022-24772).

Datos y las comprobaciones que los respaldan

Nada de lo que sigue está copiado de la documentación. La comprobación content/libraries/verify/node-forge/run.sh ejecutó node-forge 1.4.0 en Node.js 24.22.0 en la imagen de contenedor node:24.22.0-bookworm-slim el 20 de octubre de 2026; la página debe coincidir con lo que imprimió, y cada dato cita esas líneas.

  • Curvas ECDSA

    none

    Sin implementación de ECDSA: forge.pki tiene rsa y ed25519, y nada para las curvas NIST ni secp256k1.

    Registrado por la comprobación
    fact ecdsa-curves = none
    evidence ecdsa-curves: forge.pki members: ed25519, rsa
  • Certificados con claves EC

    rejected

    certificateFromPem() con un certificado P-256 lanza Cannot read public key. OID is not RSA.; una clave pública EC provoca Cannot read public key. Unknown OID.

    Registrado por la comprobación
    fact ec-certificate = rejected
    evidence ec-certificate: Cannot read public key. OID is not RSA.
    evidence ec-certificate: the same certificate parses at the ASN.1 level: true
    evidence ec-certificate: publicKeyFromPem on an EC SPKI: Cannot read public key. Unknown OID.
  • EdDSA

    Ed25519

    Ed25519 mediante forge.pki.ed25519.

    Registrado por la comprobación
    fact eddsa = Ed25519
  • Ed25519 coincide con el RFC 8032

    yes

    Con la misma semilla de 32 bytes, forge produjo exactamente la misma firma que node:crypto, como exige el esquema determinista del RFC 8032.

    Registrado por la comprobación
    fact ed25519-matches-rfc8032 = yes
    evidence ed25519-matches-rfc8032: forge and node:crypto signatures for the same seed are identical: true
  • Estructura de la clave privada Ed25519

    expanded-64

    Las claves privadas Ed25519 ocupan 64 bytes: la semilla seguida de la clave pública.

    Registrado por la comprobación
    fact ed25519-private-key = expanded-64
  • Firma Ed25519 con S no canónico

    rejected

    Se rechazó una firma Ed25519 cuyo S se había sustituido por S + L —la forma maleable que prohíbe el RFC 8032—, igual que en node:crypto; la versión 1.4.0 añadió esta comprobación (CVE-2026-33895).

    Registrado por la comprobación
    fact ed25519-non-canonical-s = rejected
    evidence ed25519-non-canonical-s: forge: false; node:crypto: false
  • Algoritmos poscuánticos

    none

    Sin algoritmos poscuánticos.

    Registrado por la comprobación
    fact pqc = none
  • Esquemas de firma RSA

    PKCS1-v1_5PSS

    Las firmas PKCS#1 v1.5 y PSS hechas con SHA-256 se verificaron en node:crypto; PSS requiere una longitud de sal y un resumen MGF1 explícitos.

    Registrado por la comprobación
    fact rsa-signature-schemes = PKCS1-v1_5, PSS
  • Elemento adicional en un DigestInfo de PKCS #1

    accepted-in-algorithm-identifier

    Un bloque PKCS#1 v1.5 con un elemento adicional dentro del AlgorithmIdentifier de DigestInfo se verificó en forge, pero no en node:crypto; un elemento adicional después del resumen se rechazó. Es el aviso abierto GHSA-86w9-cpqp-85rv.

    Registrado por la comprobación
    fact pkcs1-digestinfo-extra-element = accepted-in-algorithm-identifier
    evidence pkcs1-digestinfo-extra-element: well-formed block: forge true, node:crypto true
    evidence pkcs1-digestinfo-extra-element: extra element inside AlgorithmIdentifier: forge true, node:crypto false
    evidence pkcs1-digestinfo-extra-element: extra element after the digest: forge throws ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value., node:crypto false
  • Formatos de clave pública RSA

    spki-pempkcs1x509

    Las claves públicas RSA se cargaron desde SPKI PEM, PKCS#1 RSA PUBLIC KEY PEM y certificados.

    Registrado por la comprobación
    fact rsa-public-key-formats = spki-pem, pkcs1, x509
  • Formatos de clave privada RSA

    pkcs8-pemencrypted-pkcs8pkcs1pkcs12

    Las claves privadas RSA se cargaron desde PKCS#8 PEM, PKCS#8 cifrado (decryptRsaPrivateKey), PKCS#1 RSA PRIVATE KEY PEM y PKCS#12.

    Registrado por la comprobación
    fact rsa-private-key-formats = pkcs8-pem, encrypted-pkcs8, pkcs1, pkcs12

Uso

Firma con RSA-PSS en forge y verifica en node:crypto con los mismos parámetros; después, muestra qué ocurre con una clave EC.
import { constants, generateKeyPairSync, verify } from "node:crypto";
import forge from "node-forge";

const message = "order #1042 approved";
const { privateKey, publicKey } = generateKeyPairSync("rsa", { modulusLength: 2048 });
const key = forge.pki.privateKeyFromPem(privateKey.export({ type: "pkcs8", format: "pem" }));

// RSA-PSS: SHA-256 for the digest and for MGF1, 32-byte salt. The verifier
// must use the same three parameters.
const sha256 = () => forge.md.sha256.create();
const pss = forge.pss.create({ md: sha256(), mgf: forge.mgf.mgf1.create(sha256()), saltLength: 32 });
const signature = Buffer.from(key.sign(sha256().update(message, "utf8"), pss), "binary");
const options = { key: publicKey, padding: constants.RSA_PKCS1_PSS_PADDING, saltLength: 32 };
console.log("PSS verified by node:crypto:", verify("sha256", Buffer.from(message), options, signature));

// forge has no elliptic-curve support: EC keys and certificates throw.
const ec = generateKeyPairSync("ec", { namedCurve: "P-256" }).publicKey.export({ type: "spki", format: "pem" });
try {
  forge.pki.publicKeyFromPem(ec);
} catch (error) {
  console.log("EC key:", error.message);
}

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

Configuración
npm init -y >/dev/null && npm install --no-audit --no-fund node-forge@1.4.0
Comando
node usage.mjs
La salida debe coincidir (expresión regular)
PSS verified by node:crypto: true\nEC key: Cannot read public key\. Unknown OID\.

Bibliotecas