node:crypto — криптографический модуль, который входит в каждый релиз Node.js. Его одношаговые функции crypto.sign() и crypto.verify() и класс KeyObject поддерживают ECDSA, Ed25519, Ed448, RSA, а начиная с Node.js 24.22 — также ML-DSA, SLH-DSA и ML-KEM. Всё работает поверх OpenSSL, встроенного в Node.js (3.5.8 в Node.js 24.22.0), поэтому поведение в основном повторяет командную строку OpenSSL — и отличается от WebCrypto API, который тот же процесс предоставляет как crypto.subtle.
Самая частая ошибка совместимости — кодировка подписи. crypto.sign() возвращает ASN.1 DER, а WebCrypto, JWS/JWT, .NET и большинство аппаратных кошельков используют формат raw r‖s фиксированной длины. Подпись в другой кодировке не вызывает исключения: crypto.verify() просто возвращает false, и это выглядит точно так же, как поддельная подпись. Задайте dsaEncoding: "ieee-p1363" в объекте ключа и на стороне подписи, и на стороне проверки или преобразуйте байты конвертером DER ⇄ raw.
Ещё две, менее заметные, настройки по умолчанию важны, когда другая сторона проверяет строго. Подписи никогда не нормализуются к low-S, поэтому примерно половину из них отвергают реализации проверки, которые требуют low-S, например libsecp256k1 и @noble/curves 2.x. Кроме того, nonce случаен, поэтому один и тот же ключ с одним и тем же сообщением при каждом вызове дают разные байты: в тестах сравнивайте результаты проверки и никогда не сравнивайте байты подписей. Если подписант хоть раз повторит nonce, проверка повторного использования nonce восстановит ключ, поэтому случайному nonce нужен надёжный источник случайности.
Если передать null в качестве алгоритма, подпись ключом EC молча создаётся с SHA-256 независимо от кривой; тогда подписи ключом P-384 не пройдут проверку на любой стороне, которая ожидает SHA-384. Указывайте хеш в каждом вызове, загружайте ключи один раз через crypto.createPublicKey() или crypto.createPrivateKey() — они читают PEM, DER, JWK и даже голую точку SEC1 — и храните полученный KeyObject, а не разбирайте строки заново.
Чтобы проверить подпись, созданную в другом месте, вставьте её вместе с ключом в инструмент проверки подписей; чтобы увидеть, какие настройки по умолчанию конфликтуют с другим стеком, выберите обе стороны в матрице совместимости библиотек.