node:crypto es el módulo de criptografía que se distribuye con cada versión de Node.js. Sus funciones de una sola llamada crypto.sign() y crypto.verify() y la clase KeyObject cubren ECDSA, Ed25519, Ed448, RSA y, en Node.js 24.22, también ML-DSA, SLH-DSA y ML-KEM. Todo funciona sobre el OpenSSL que incluye Node.js (3.5.8 en Node.js 24.22.0), así que la mayor parte del comportamiento coincide con el de la línea de comandos de OpenSSL y difiere del de la API WebCrypto que el mismo proceso expone como crypto.subtle.
El problema de interoperabilidad con el que más probablemente te encontrarás es la codificación de la firma. crypto.sign() devuelve DER ASN.1, mientras que WebCrypto, JWS/JWT, .NET y la mayoría de las billeteras de hardware usan la forma r‖s en bruto de ancho fijo. Una firma en la otra codificación no lanza ninguna excepción: crypto.verify() simplemente devuelve false, algo indistinguible de una firma falsificada. Establece dsaEncoding: "ieee-p1363" en el objeto de clave tanto en el lado que firma como en el que verifica, o convierte los bytes con el conversor DER ⇄ en bruto.
Otros dos valores predeterminados, menos visibles, importan cuando el otro lado es estricto. Las firmas nunca se normalizan a low-S, así que los verificadores que lo exigen, como libsecp256k1 y @noble/curves 2.x, rechazan aproximadamente la mitad. Y el nonce es aleatorio, de modo que la misma clave y el mismo mensaje dan bytes distintos en cada llamada: en las pruebas, compara los resultados de la verificación, nunca los bytes de la firma. Si un firmante llega a repetir un nonce, el comprobador de reutilización de nonces recupera la clave; por eso un nonce aleatorio necesita una fuente de aleatoriedad fiable.
Si pasas null como algoritmo, una clave EC firma en silencio con SHA-256, sea cual sea su curva; una clave P-384 falla entonces con cualquier implementación que espere SHA-384. Indica el hash en cada llamada, carga las claves una sola vez con crypto.createPublicKey() o crypto.createPrivateKey() —leen PEM, DER, JWK e incluso el punto SEC1 sin envoltorio— y conserva el KeyObject resultante en lugar de volver a analizar cadenas.
Para comprobar una firma generada en otra parte, pégala junto con la clave en el verificador de firmas; para ver qué valores predeterminados chocan con otra pila, elige ambos lados en la matriz de interoperabilidad de bibliotecas.