O node:crypto é o módulo de criptografia que acompanha todas as versões do Node.js. As funções de chamada única crypto.sign() e crypto.verify() e a classe KeyObject cobrem ECDSA, Ed25519, Ed448, RSA e, a partir do Node.js 24.22, ML-DSA, SLH-DSA e ML-KEM. Tudo roda sobre o OpenSSL embutido no Node.js (3.5.8 no Node.js 24.22.0), então a maior parte do comportamento espelha a linha de comando do OpenSSL — e difere da API WebCrypto que o mesmo processo expõe como crypto.subtle.
O bug de interoperabilidade que você tem mais chance de encontrar é a codificação da assinatura. crypto.sign() retorna ASN.1 DER, enquanto WebCrypto, JWS/JWT, .NET e a maioria das carteiras de hardware usam a forma r‖s bruta de tamanho fixo. Uma assinatura na outra codificação não lança exceção: crypto.verify() simplesmente retorna false, o que parece exatamente uma assinatura falsificada. Defina dsaEncoding: "ieee-p1363" no objeto de chave tanto no lado que assina quanto no lado que verifica, ou converta os bytes com o conversor DER ⇄ bruto.
Dois padrões menos visíveis fazem diferença quando o outro lado é rigoroso. As assinaturas nunca são normalizadas para low-S, então cerca de metade delas é rejeitada por verificadores que exigem isso, como a libsecp256k1 e a @noble/curves 2.x. E o nonce é aleatório, então a mesma chave e a mesma mensagem produzem bytes diferentes a cada chamada — nos testes, compare resultados de verificação, nunca os bytes da assinatura. Se um signatário algum dia repetir um nonce, o verificador de reutilização de nonce recupera a chave, e é por isso que um nonce aleatório precisa de uma boa fonte de aleatoriedade.
Se você passar null como algoritmo, uma chave EC assina silenciosamente com SHA-256, qualquer que seja a curva; uma chave P-384 então falha com qualquer contraparte que espere SHA-384. Indique o hash em toda chamada, carregue as chaves uma única vez com crypto.createPublicKey() ou crypto.createPrivateKey() — elas leem PEM, DER, JWK e até o ponto SEC1 isolado — e guarde o KeyObject resultante em vez de analisar strings de novo.
Para conferir uma assinatura produzida em outro lugar, cole-a com a chave no verificador de assinaturas; para ver quais padrões entram em conflito com outra stack, escolha os dois lados na matriz de interoperabilidade de bibliotecas.