跳到主要内容
ecdsa.com
Node.js

node-forge

Digital Bazaar 的 node-forge,纯 JavaScript 实现的 TLS、PKI 和 PKCS 工具包:支持 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 最初是一个 JavaScript TLS 实现,如今主要用于它的 PKI 部分:生成和解析 X.509 证书与 CSR、读写 PKCS#12 包、RSA 加密和签名,以及 ASN.1 解析器。它在浏览器和 Node.js 中的行为相同,不含原生代码。

除 Ed25519 之外,它不支持任何椭圆曲线。forge.pki 只提供 rsa 和 ed25519,检查也证实了由此带来的后果:对 ECDSA 证书调用 certificateFromPem() 会抛出 Cannot read public key. OID is not RSA.,对 EC 公钥调用 publicKeyFromPem() 会抛出 Cannot read public key. Unknown OID.——尽管同样的字节用 forge 自己的 ASN.1 读取器可以正常解析。检查真实环境中证书的代码随时都会遇到 ECDSA 密钥,需要另一个解析器来处理它们;node:crypto 的 X509Certificate 或证书解码器都能处理这两种密钥类型。

对于 RSA,只要参数一致,它就能顺利互通。forge 生成的 PKCS#1 v1.5 和 PSS 签名能用 node:crypto 验证通过;PSS 要求两端的摘要算法、MGF1 摘要算法和盐长度一致,forge 要求你像示例那样明确指定这三项。公钥可以从 SPKI、PKCS#1 和证书 PEM 加载,私钥可以从 PKCS#8、加密的 PKCS#8、PKCS#1 和 PKCS#12 加载——在 PFX 查看器中打开这些包即可查看其中的内容。

对于同一个种子,forge 生成的 Ed25519 签名与 node:crypto 生成的逐字节相同。它的私钥是 64 字节的扩展形式——种子后接公钥——因此要求 32 字节种子的库只需要前一半。

签名验证历来存在解析过于宽松的问题,其中一例至今尚未修复。2026年9月发布的安全公告 GHSA-86w9-cpqp-85rv(CVE-2026-85393)尚无修复版本,它指出 1.4.0 及以下版本的 RSA PKCS#1 v1.5 验证会忽略 DigestInfo 算法标识符中的多余元素,使得在公钥指数较小(例如 3)的密钥下可以伪造签名。检查复现了这种宽松行为:在该位置带有多余元素、填充正确的数据块在 forge 中验证通过,而被 node:crypto 拒绝。在修复版本发布之前,应使用 node:crypto 或 WebCrypto 验证 PKCS#1 v1.5 签名,并避免使用指数为 3 的 RSA 密钥。1.4.0 本身修复了上一轮问题——摘要之后的多余字段和非规范的 Ed25519 签名都会被拒绝——而 1.3.0 之前的版本还存在更早的签名伪造漏洞(CVE-2022-24771、CVE-2022-24772)。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/node-forge/run.sh 于2026年10月20日在容器镜像 node:24.22.0-bookworm-slim 中运行了 node-forge 1.4.0(运行时为 Node.js 24.22.0);本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • ECDSA 曲线

    none

    没有 ECDSA 实现:forge.pki 只有 rsa 和 ed25519,没有任何针对 NIST 曲线或 secp256k1 曲线的实现。

    检查记录
    fact ecdsa-curves = none
    evidence ecdsa-curves: forge.pki members: ed25519, rsa
  • 使用 EC 密钥的证书

    rejected

    对 P-256 证书调用 certificateFromPem() 会抛出 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

    通过 forge.pki.ed25519 支持 Ed25519。

    检查记录
    fact eddsa = Ed25519
  • Ed25519 是否符合 RFC 8032

    yes

    对于同一个 32 字节的种子,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 字节:种子后接公钥。

    检查记录
    fact ed25519-private-key = expanded-64
  • S 值非规范的 Ed25519 签名

    rejected

    把 S 替换为 S + L 的 Ed25519 签名——即 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

    用 SHA-256 生成的 PKCS#1 v1.5 和 PSS 签名在 node:crypto 中验证通过;PSS 需要显式指定盐长度和 MGF1 摘要算法。

    检查记录
    fact rsa-signature-schemes = PKCS1-v1_5, PSS
  • PKCS #1 DigestInfo 中的多余元素

    accepted-in-algorithm-identifier

    DigestInfo 的 AlgorithmIdentifier 中带有多余元素的 PKCS#1 v1.5 数据块在 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

用法

在 forge 中用 RSA-PSS 签名,在 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\.

库