Перейти к содержимому
ecdsa.com
Node.js

node-forge

node-forge от Digital Bazaar — набор инструментов для TLS, PKI и PKCS на чистом JavaScript: RSA, PKCS#12, X.509 и Ed25519, но без какой-либо поддержки ECDSA, поэтому он не может прочитать ключ или сертификат на эллиптической кривой.
Лицензия
BSD-3-Clause OR GPL-2.0
Проверенная версия
1.4.0 ·
Исходный код
github.com/digitalbazaar/forge
Документация
github.com/digitalbazaar/forge#readme

Совместимость вкратце

EdDSA

EdDSA
Ed25519

node-forge начинался как реализация TLS на JavaScript, а сегодня его используют в основном ради компонентов PKI: генерации и разбора сертификатов X.509 и CSR, чтения и записи пакетов PKCS#12, шифрования и подписей RSA, а также парсера ASN.1. Он одинаково работает в браузерах и Node.js и не содержит нативного кода.

Эллиптические кривые, кроме Ed25519, не поддерживаются. forge.pki предоставляет только rsa и ed25519, и проверка подтвердила следствие: certificateFromPem() на сертификате ECDSA выбрасывает Cannot read public key. OID is not RSA., а publicKeyFromPem() на открытом ключе EC выбрасывает Cannot read public key. Unknown OID. — хотя собственный парсер ASN.1 в forge без проблем разбирает те же байты. Коду, который разбирает сертификаты, встречающиеся на практике, ключи ECDSA попадаются постоянно, и для них нужен другой парсер; X509Certificate из node:crypto и декодер сертификатов работают с обоими типами ключей.

С RSA библиотека без проблем совместима, если параметры совпадают. Подписи PKCS#1 v1.5 и PSS, созданные в forge, прошли проверку в node:crypto; для PSS дайджест, дайджест MGF1 и длина соли должны совпадать на обеих сторонах, и forge требует указать все три, как это делает пример. Открытые ключи загружаются из PEM с SPKI и PKCS#1 и из PEM-сертификатов, закрытые ключи — из PKCS#8, зашифрованного PKCS#8, PKCS#1 и PKCS#12; чтобы увидеть содержимое пакетов, откройте их в инспекторе PFX.

Подписи Ed25519 из forge при одном и том же seed побайтно совпали с подписями node:crypto. Закрытый ключ в forge хранится в расширенной 64-байтовой форме: сначала seed, затем открытый ключ, поэтому библиотекам, которые ожидают 32-байтовый seed, нужна только первая половина.

Проверка подписей в forge уже не раз страдала от нестрогого разбора, и один случай всё ещё не закрыт. Бюллетень безопасности GHSA-86w9-cpqp-85rv (CVE-2026-85393), опубликованный в сентябре 2026 года без релиза с исправлением, сообщает, что проверка RSA PKCS#1 v1.5 в версиях до 1.4.0 включительно игнорирует лишние элементы внутри идентификатора алгоритма в DigestInfo, из-за чего подписи можно подделать для ключей с малой открытой экспонентой, например 3. В ходе нашей проверки это поведение воспроизвелось: правильно дополненный блок с лишним элементом в этом месте прошёл проверку в forge и был отклонён в node:crypto. Пока исправление не выпущено, проверяйте подписи PKCS#1 v1.5 через node:crypto или WebCrypto и не используйте ключи RSA с экспонентой 3. Предыдущую серию проблем исправила сама версия 1.4.0: в ней и лишнее поле после дайджеста, и неканонические подписи Ed25519 были отклонены, а в версиях до 1.3.0 были более ранние ошибки, позволявшие подделывать подписи (CVE-2022-24771, CVE-2022-24772).

Факты и проверки, на которых они основаны

Ничего из приведённого ниже не скопировано из документации. 20 октября 2026 г. проверка content/libraries/verify/node-forge/run.sh запустила node-forge 1.4.0 в среде Node.js 24.22.0 в образе контейнера node:24.22.0-bookworm-slim; содержимое страницы должно совпадать с выводом проверки, а у каждого факта приведены соответствующие строки вывода.

  • Кривые ECDSA

    none

    Реализации ECDSA нет: в forge.pki есть rsa и ed25519, но ничего для кривых NIST или secp256k1.

    Что записала проверка
    fact ecdsa-curves = none
    evidence ecdsa-curves: forge.pki members: ed25519, rsa
  • Сертификаты с ключами EC

    rejected

    certificateFromPem() на сертификате P-256 выбрасывает Cannot read public key. OID is not RSA.; для открытого ключа EC выбрасывается Cannot read public key. Unknown OID.

    Что записала проверка
    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 через forge.pki.ed25519.

    Что записала проверка
    fact eddsa = Ed25519
  • Соответствие Ed25519 стандарту RFC 8032

    yes

    При одном и том же 32-байтовом seed forge создал в точности ту же подпись, что и node:crypto, как того требует детерминированная схема RFC 8032.

    Что записала проверка
    fact ed25519-matches-rfc8032 = yes
    evidence ed25519-matches-rfc8032: forge and node:crypto signatures for the same seed are identical: true
  • Структура закрытого ключа Ed25519

    expanded-64

    Закрытые ключи Ed25519 занимают 64 байта: seed, за которым следует открытый ключ.

    Что записала проверка
    fact ed25519-private-key = expanded-64
  • Подпись Ed25519 с неканоническим S

    rejected

    Подпись Ed25519, в которой S заменили на S + L (такую изменённую форму, возможную из-за пластичности, запрещает RFC 8032), была отклонена так же, как в node:crypto; эту проверку добавила версия 1.4.0 (CVE-2026-33895).

    Что записала проверка
    fact ed25519-non-canonical-s = rejected
    evidence ed25519-non-canonical-s: forge: false; node:crypto: false
  • Постквантовые алгоритмы

    none

    Постквантовых алгоритмов нет.

    Что записала проверка
    fact pqc = none
  • Схемы подписи RSA

    PKCS1-v1_5PSS

    Подписи PKCS#1 v1.5 и PSS с SHA-256 прошли проверку в node:crypto; для PSS нужно явно указать длину соли и дайджест MGF1.

    Что записала проверка
    fact rsa-signature-schemes = PKCS1-v1_5, PSS
  • Лишний элемент в DigestInfo PKCS #1

    accepted-in-algorithm-identifier

    Блок PKCS#1 v1.5 с лишним элементом внутри AlgorithmIdentifier в DigestInfo прошёл проверку в forge, но не в node:crypto; лишний элемент после дайджеста был отклонён. Это проблема из открытого бюллетеня безопасности GHSA-86w9-cpqp-85rv.

    Что записала проверка
    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
  • Форматы открытых ключей RSA

    spki-pempkcs1x509

    Открытые ключи RSA загрузились из SPKI в PEM, из PKCS#1 RSA PUBLIC KEY в PEM и из сертификатов.

    Что записала проверка
    fact rsa-public-key-formats = spki-pem, pkcs1, x509
  • Форматы закрытых ключей RSA

    pkcs8-pemencrypted-pkcs8pkcs1pkcs12

    Закрытые ключи RSA загрузились из PKCS#8 в PEM, из зашифрованного PKCS#8 (decryptRsaPrivateKey), из PKCS#1 RSA PRIVATE KEY в PEM и из PKCS#12.

    Что записала проверка
    fact rsa-private-key-formats = pkcs8-pem, encrypted-pkcs8, pkcs1, pkcs12

Использование

Подпишите данные RSA-PSS в forge и проверьте подпись в node:crypto с теми же параметрами, а затем посмотрите, что происходит с ключом 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);
}

Тестовый стенд для фрагментов кода запускает каждый пример без изменений в образе контейнера node:24.22.0-bookworm-slim, и запуск завершается ошибкой, если вывод не совпадает.

Подготовка
npm init -y >/dev/null && npm install --no-audit --no-fund node-forge@1.4.0
Команда
node usage.mjs
Вывод должен совпадать (регулярное выражение)
PSS verified by node:crypto: true\nEC key: Cannot read public key\. Unknown OID\.

Библиотеки