You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

node:crypto中RSA的createSign与privateEncrypt差异解析

RSA签名与私钥加密的差异解析

核心差异:签名 vs 私钥加密

虽然privateEncrypt()和createSign()默认都标注使用RSA_PKCS1_PADDING,但它们对应的是PKCS#1标准中两种完全不同的填充方案:

  • createSign()实现的是RSASSA-PKCS1-v1_5签名机制:专为签名场景设计,会对哈希值做额外的ASN.1格式封装。
  • privateEncrypt()实现的是RSAES-PKCS1-v1_5加密机制:专为加密场景设计,直接对输入数据做填充加密。

具体流程拆解

1. createSign()的签名流程

调用createSign("SHA256").update(...).sign(...)时,实际执行步骤为:

  1. 对原始消息计算SHA256哈希值;
  2. 将哈希值封装成ASN.1格式的DigestInfo结构(包含哈希算法标识和哈希值);
  3. 用私钥对这个DigestInfo结构做RSASSA-PKCS1-v1_5填充加密,得到最终签名。

其中SHA256对应的DigestInfo固定前缀为十六进制的3031300d060960864801650304020105000420,拼接哈希值后才是签名的真正输入。

2. privateEncrypt()的加密流程

privateEncrypt(privateKey, hash)的操作逻辑是:

  1. 直接将输入的哈希值作为明文;
  2. 用RSAES-PKCS1-v1_5填充方案对明文做私钥加密,得到结果。

因为缺少DigestInfo封装这一步,两者输出自然不同。

修正代码:让两者结果一致

如果要让privateEncrypt()的输出和createSign()的签名结果一致,需要手动构造DigestInfo结构,再传入privateEncrypt():

// 仅用于演示,请勿在生产环境使用
import {
    generateKeyPairSync, createSign, privateEncrypt, createVerify, createHash
} from "node:crypto";

const keyPair = generateKeyPairSync("rsa", {modulusLength: 1024});
const message = "stack overflow";

// 1. 用createSign生成标准签名
const signed = createSign("SHA256").update(message).end().sign(keyPair.privateKey);

// 2. 手动构造DigestInfo并通过privateEncrypt生成相同结果
const hash = createHash('sha256').update(message).digest();
// SHA256对应的DigestInfo ASN.1前缀(十六进制转Buffer)
const digestInfoPrefix = Buffer.from("3031300d060960864801650304020105000420", "hex");
// 拼接前缀和哈希值,得到完整的DigestInfo
const digestInfo = Buffer.concat([digestInfoPrefix, hash]);
// 用privateEncrypt加密DigestInfo,得到和签名一致的结果
const encrypted = privateEncrypt(keyPair.privateKey, digestInfo);

// 现在断言会成功
console.assert(!Buffer.compare(encrypted, signed), "结果不一致");
console.log("结果一致,验证通过");

// 验证该结果也能被公钥验证
const verifyResult = createVerify("SHA256")
    .update(message)
    .end()
    .verify(keyPair.publicKey, encrypted);
console.log("验证结果:", verifyResult); // 输出 true

关于填充方案的疑问解答

privateEncrypt()默认使用的是加密版填充(RSAES-PKCS1-v1_5),而签名操作(createSign())使用的是签名版填充(RSASSA-PKCS1-v1_5)。Node.js文档未明确标注这一点,但这是PKCS#1标准的定义:两种场景的填充逻辑不同,不能混用。


内容的提问来源于stack exchange,提问作者Sergey Kaunov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 17:25:25