钱包要给节点一个授权证据:节点必须能独立检查“这组明确字节经某个有权的私钥持有人签署”,却不应该因此获得生成新授权的能力。04 篇的 HMAC 做不到后一条,因为接收者也持有同一把密钥。数字签名提供另一种分工:私钥参与生成签名,公钥参与验证签名。但是验签函数返回 true 仍有至少两个问题悬而未决:这个公钥属于谁?这段被签的字节是否就是需要授权的那件事?

这篇用成熟库分别运行 RSA-PSS、P-256 上的 ECDSA 与 Ed25519 的正负验签,严格区分“原语实验通过”和“业务或链上的授权已经生效”。

三个输入,不是一句“签过了”

发送者在既定算法与编码约定下对确定字节生成签名;验证者必须同时拥有消息字节、签名和可信来源的正确公钥,还要知道怎样解码签名与选择算法参数。对恶意者自造密钥并签署的消息,verify(恶意者的公钥, 消息, 签名) 也能返回真,不能因此推断发件人是合法钱包主人或预期网站。

1
2
3
4
5
6
7
甲:私钥(不得外发) + 确定的消息字节 -> 签名
| |
v v
节点:可信规则 -> 预期公钥 ----> 验证签名 ----> 是/否
↑
若此处直接采用攻击者随消息附带的公钥,
“验签成功”就失去了身份归属的意义。

这个结构没有把消息加密。订单、交易和签名通常可见;私钥对签名过程的作用也不是对任意文本做“私钥加密”。RSA 数学里加密与签名确有相关的模幂操作,但两者的安全编码和协议含义完全不同。RSA-PSS 是带哈希与随机盐规则的签名方案,不能让本文把 pow(message, private_exponent, modulus) 直接当成安全签名,更不能拿 RSA 公钥验签等同于解密订单。

同一实验为什么放三种算法

本次 RSA-PSS 生成 2048 位实验密钥,签名前后都按 SHA-256 与 32 字节 PSS 盐长度配置。ECDSA 使用 NIST P-256 与 SHA-256,签名从库返回 DER 编码;DER 如何表达两个签名整数,和区块链对签名字节的要求是不同层次的问题。Ed25519 调用 Node sign(null, bytes, key):这里按 Ed25519 方案签完整消息,不在 API 外侧再手工 SHA-256 一次当通用“签名预处理”。三个测试都为临时合成身份和公开订单文本,不是 TLS 服务器证书或链上账户签名。

为何不把 ECDSA 那组结果直接当 Ethereum 交易验收?本实验的曲线是 P-256;Ethereum 执行层账户签名按其交易规范使用不同的曲线、消息格式与验证规则,Bitcoin 又有自己的锁定条件与 sighash。Ed25519 在别的体系出现,也不能从算法名字推出链或令牌格式兼容。12 的目标是让读者能指出私钥/公钥、签名输入和失败判据;真实交易的具体字节留到 27、28 分别核对。

RFC 8032 §7.1 的公开 Ed25519 向量为另一个独立锚点:已公布的样例私有测试字节 9d61... 对空消息生成固定公钥 d75a... 与签名 e556...。这份测试“私钥”是人人都能读到的公开资料,不具备任何实际保密价值,绝不能在真实系统中使用。测试逐字节比较规范给出的公钥与 64 字节签名;随机生成的真正临时实验私钥从未进入 Git 或公开输出。RSA-PSS、ECDSA 在本章只验证正确/错误输入路径,尚缺它们独立的官方逐字节向量,这个缺口不会被 Ed25519 向量代替。

改字节、改身份、改用途分别会怎样

实验订单写成 lab:transaction:v1\0order=demo-001;quantity=1。这里的前缀只是教学用的域分离:让不同用途的信息即便业务字段相同,也不共享完全一样的签名字节。改数量到 9,旧签名不通过;把 transaction 改成 login,Ed25519 同样拒绝;使用另一对临时密钥的公钥,也拒绝;翻转 Ed25519 签名字节,依然拒绝。

1
2
3
4
5
签署:    lab:transaction:v1\0order=demo-001;quantity=1
验证 A: 相同消息 + 原公钥 ---------------------------> 通过
验证 B: 相同前缀 + quantity=9 ---------------------> 拒绝
验证 C: lab:login:v1\0 后面保持原字段 -------------> 拒绝
验证 D: 原消息 + 另一公钥 --------------------------> 拒绝

应用采用前缀时,必须由验证方按自身用途政策确定预期域,不能只是把攻击者传来的 domain 原样交给验证函数。即使域正确、签名有效,仍可能重放旧的有效请求;还要检查过期时间、唯一值与已消费状态。28 的 EIP-712 类型化签名有具体域构造,但规范自身不实现重放消费。数字签名也不会自动证明现实订单交付、交易可执行或已确认。

真实原语证据而非完整协议实验

1
2
node examples/cryptography/12_signatures.mjs
node --test examples/cryptography/12_signatures.test.mjs

2026-10-06 UTC,在 Node 22.23.2 / 内置 OpenSSL 3.5.7 上两条命令退出码均为 0,2 个测试通过。RFC 8032 官方向量逐字节一致;RSA-PSS、ECDSA、Ed25519 对原消息/对应公钥返回真,对错误消息/错误公钥返回拒绝,Ed25519 的错误域和改动后的签名同样被拒绝。运行时 RSA-PSS 盐与随机密钥不同,实验不发布任何新生成私钥或要求每次签名的字节相同。精确输出、库与尚未冻结的独立 RSA/ECDSA 向量缺口记录在 writing-plans/cryptography/research/12.md。

如果把实验里错误公钥换成当前签名相匹配的正确公钥,拒绝断言会失败;这是负例缺失,不是身份从此可信。可信服务公钥为何关联域名、节点在账本里从哪里确定有权公钥,这两个不同的“公钥来源”问题在 13–14、26–28 再分开解决。

两道带答案的练习

画图题。 节点收到 order_id、签名及一个随消息附带的公钥。攻击者能新建一对密钥并重签同一订单。请指出即使 verify 返回真,至少还缺哪两项检查;哪一端知道私钥?

可核对答案: 私钥只有攻击者本人(若它是恶意自签者)知道;节点至少要由可信规则确认哪个公钥有权授权这笔交易,并核对链和交易类型规定的确切字节是否包含订单相关意图。还须检查交易状态、重放约束与链上规则。仅靠攻击者附送公钥的验签不证明合法钱包授权。

变更题。 将 12_signatures.mjs 的 wrongDomain 改成与签署消息完全一样,运行脚本与测试。哪个预期会失败?把 RSA 的 PSS 验证盐长度改成一个不匹配值,会改变“私钥加密”这种说法的对错吗?

可核对答案: ed25519_wrong_domain_rejected 会变为假,脚本拒绝断言触发;独立的测试调用仍按源码生成新的 wrongDomain,应同步检查实验输入,不能只看测试退出码。PSS 验证参数不符可能导致验签失败,正说明签名有明确方案与参数;它并不让“私钥加密任意消息”变成正确解释。

资料与导航