02 篇解决了“哪组字段变成哪串字节”。有了字节,签名才能有确定对象;有了签名,仍不能推出线下身份、输入未花费或业务已交付。01 篇的两笔冲突意图正好检验这条边界:同一私钥可以对两个收款方分别签字。

先修:区块链(02):哈希之前,先说清究竟签了哪些字节 中的确定性字节编码;懂得摘要不是身份。系统模型是隔离的本机 OpenSSL 3.0.13,私钥由工具生成在自动清除的临时目录,公钥从私钥导出。这里没有钱包、链、RPC 或可绑定现实用户的身份证明。

验证的是签名字节,不是交易结果

sequenceDiagram
  participant A as 签名方(临时私钥)
  participant V as 校验方(公钥)
  A->>A: encode(教学域,订单,收款方,输入,金额)
  A->>V: 消息与 ECDSA-SHA256 签名
  V->>V: 针对这一条消息验签
  V-->>A: Verified OK / 失败
  Note over V: 尚未查询账本、订单或交付凭证

实验先生成 secp256k1 密钥和公钥,再分别签署卖家收款与攻击者收款两个意图:openssl dgst -sha256 -sign ... 与 openssl dgst -sha256 -verify ... 对对应消息均返回 Verified OK;把第一笔签名交叉拿去验证第二笔消息则退出码非零。test_models.py 使用 TemporaryDirectory,不保存真实密钥、助记词或签名二进制。证据目录保存测试输出、代码 SHA、环境与退出状态,并不输出私钥。

这证明什么?在该密码学算法和测试密钥未泄露的前提下,公钥可以验证签名与给定字节相配。它不告诉收款方此输入能否消费、买家是否拥有线下身份、谁能撤回凭证,也不保证两个用户永远不使用同一个签名域。ECDSA 的随机数生成、私钥泄露与权限撤销属于签名系统的运维风险;不要因本例能签就自己实现曲线运算或用于生产钱包。

地址也不能简单等同于“公钥的缩写”。不同链、不同交易输出类型和版本有不同的编码或锁定条件;有些地址承诺的是脚本或程序条件而非裸公钥。看见一个地址必须先问网络、版本、验证路径、密钥备份责任和离线恢复流程。03 篇没有运行地址生成、HD 派生和恢复;它们应在 08 篇与冻结版本的 Bitcoin 钱包中实测。

生命周期可按产生 → 限定用途 → 使用 → 轮换/撤销 → 备份与恢复拆开。不要提交私钥、种子词、完整钱包目录或生产签名样本。即使签名本身可公开,带真实用户消息的测试向量也可能泄露业务隐私。本篇没有持久化临时密钥,所以不能拿“清理成功”当成合格的备份/恢复试验。

同一段业务授权若不写订单号、操作名、有效期和消费序号,签名即便只流出一次,也可能被拿到另一份订单上重放。这里显式写了教学域与 synthetic-001,却没有实现截止时间或授权撤销;若把该模型迁移到合约,不能保留这些空缺再称“应用防重放已通过”。密钥生命周期与交易生命周期也不同:删除本地私钥不会使已经广播、且曾被验证过的授权自动失效;合约级撤销必须在可验证状态里定义。

签名和地址之外还需要可信的公钥取得路径。校验器若直接相信请求携带的任意公钥,只能证明“消息出自这个新给出的密钥”,不能证明它属于买家。要把公钥、地址和订单的授权关系提前持久化或由有信任根的签发机制提供,并让用户知道撤销和恢复后旧权限怎样处理。

两道练习

  1. 推导:对订单 synthetic-001 同一个输入,两份不同收款方的消息都通过验签,系统应怎样决定能否付款?答案:先针对当前状态检查输入未消费,再按网络排序规则执行;不能让签名校验器单独决定先后,也不能把“签过”当作最终确认。
  2. 篡改:签名后只把收款人从 seller 改为 attacker,不重新签,预计什么结果?答案:编码字节变了,拿旧签名验证新消息失败;如果原签名人重新签署新消息则可以再次验签,但这是另一笔冲突意图。实际交叉验证在测试中非零退出。

可迁移原则:签名是“某把钥匙授权了特定字节”的证据;业务必须额外检查权限范围、消费状态、过期/撤销、链身份及现实世界的履约证据。

参考资料与核验边界

系列导航:区块链(02):哈希之前,先说清究竟签了哪些字节 · 03 签名与密钥 · 区块链(04):Merkle 包含证明需要可信根;全系列入口 区块链(00):从中心化订单基线开始。