第 25 篇:推理引擎的签名——证明"这是我发的"(零门槛入门系列)
上一篇:第 24 篇《加密防偷看,认证防偷改》 | 下一篇:第 26 篇《从发布方到设备的信任链》
一句话导读:在推理引擎里,私钥只有发布方有,用它签;公钥人人都有,用它验——签名把"这份东西确实出自我手"变成任何人都能独立核对的一件事。
关键词:推理引擎入门、大模型推理、签名(signature)、私钥(private key)、公钥(public key)、指纹(哈希)、验签(verification)
第 23 篇讲了指纹(哈希):它能证明"内容没被改过"。第 24 篇讲了加密与认证标签:一个防偷看,一个防偷改。但这两样都依赖一把双方共享的钥匙——谁能加密,谁就能伪造。于是还差最后一环:设备怎么知道这份权重文件"确实出自官方",而不是别人照着同一套格式拼出来的仿制品?签名补上这一环——它用的不是一把共享钥匙,而是一对配套的钥匙。
① 一句话概念
私钥只能签,公钥人人能验。
② 起点:公章与公示的印鉴
想象一家公司要给重要文件盖章。公司有一枚公章,锁在保险柜里,只有被授权的人才能取出来用;而公司对外发出的每一份正式文件,上面都留下同一个印迹。任何人拿到文件,都不必跑到公司去问"这真是你们发的吗"——只要把文件上的印迹,和公司对外公示的那份印鉴样本摆在一起比对,就能自己判断。
这里有两个关键性质。第一,盖章这件事只有公司做得到:印鉴样本谁都能拿到,但照着样本私刻一枚假章,盖出来的印迹细节就对不上。第二,核对这件事谁都做得到:不需要保险柜,也不需要公司配合,两份印迹放一起看即可。
数字世界里也有对应的做法:一份内容,由只有发布方持有的那把钥匙处理出一段印迹,再交给所有持有公开印鉴的人,各自核对。
③ 伪代码:一对配套的钥匙,一把签、一把验
指纹 = 哈希(内容) # 先把任意长度的内容压成固定长度指纹 签名 = 用私钥 处理(指纹) # 这一步只有发布方能做 函数 验签(内容, 签名, 公钥) -> 真 / 假: 重算指纹 = 哈希(内容) → 用公钥反算(签名) 是否等于 重算指纹 # 相等 = 真,否则 = 假逐行要点
- 先压成指纹再签(第 1 行):内容可能有几百 MB,直接用私钥处理太慢;而且指纹长度固定,签名长度也就固定。代价是"指纹算法必须与签名一起约定",两侧用了不同算法,验签必然失败。
- 只有发布方能做(第 2 行):这个处理是单向的——有私钥就能算出签名,但从签名和内容推不出私钥。这就是"只有发布方签得出"的来源。
- 公钥不是用来"解密"的(第 6 行):它的作用是反算出当初被处理的那段指纹,再和验证方自己重算的指纹作比对。两者相等,就说明签名确实对应这份内容、且确实出自私钥持有者(如图 1 所示)。
- 验证方自己重算(第 5 行):不采信对方给出的任何数字,自己动手算一遍——这是验签与"听对方声明"的本质区别。
- 每次签名都要用新的随机数:同一个随机数被用两次会泄露私钥,而随机数必须取自真正不可预测的来源。这条留到进阶篇展开。
- 身份标识(ID)参与计算:签名把"这是谁"(身份标识)和公钥一起卷进运算,所以身份标识不能随手改——改一个字符,旧签名就成批失效(如图 1 下方红框)。
图 1私钥签 / 公钥验:两侧材料对照:图分上下两排,箭头全部向右。上排是"发布方(只有它有私钥)“,向右依次是"由内容算出的内容指纹"和"签名(只有私钥算得出)”;下排是"任何人(都只有公钥)“,向右依次是"用公钥验签"和"通过 / 失败”。私钥只出现在上排那一格。
④ 纸笔实验(必做)
画一张两列表格,左列写"发布方"、右列写"设备(验证方)“,把下面五种材料分别填进两侧,判断各自"需不需要”:
材料:①私钥 ②公钥 ③内容 ④指纹 ⑤签名
预期结果(填完自查):
| 材料 | 发布方 | 设备 |
|---|---|---|
| 私钥 | 需要 | 不需要 |
| 公钥 | 需要(配对用;签名把身份与公钥一起参与计算,进阶 25-1 展开) | 需要 |
| 内容 | 需要 | 需要 |
| 指纹 | 需要(自己算) | 需要(自己算) |
| 签名 | 需要(自己生成) | 需要(拿来核对) |
判定口诀:只有"私钥"这一格,发布方打勾、设备打叉。如果你顺手在设备那一列也给私钥打了勾,说明你把"验"当成了"签"——那等于任何人都有能力伪造,签名也就失去意义了。
再加一问:把内容里的一个字改掉,哪些格子会变?答案是指纹和签名会变,公钥与私钥不变。所以"改内容 → 签名失效"是自动发生的,不需要谁盯着。
可选 REPL 版(Python 伪代码,只为体会流程,并非真实签名算法):
def指纹(内容):# 演示用;真实场景是标准哈希算法returnsum(内容.encode())%1000003def签名(内容,私钥):return指纹(内容)+私钥# 演示用;真实签名是单向运算def验签(内容,签名,公钥):return签名-公钥==指纹(内容)# 反算后与重算指纹比对私钥,公钥=7,7# 这里只为让加减法闭合;真实方案里公钥由私钥算出、不可反推s=签名("模型文件",私钥)print(验签("模型文件",s,公钥))# Trueprint(验签("模型文什",s,公钥))# False:改一个字,验签失败⑤ 进阶锚点
进阶锚点:本篇概念在《30 天手搓推理引擎》Day 25里被真正实现——
25-1 椭圆曲线公钥密码与 GM/T 0003 的 ZA/ID/25-2 工具链实操:vllm_vqf_sign 的 genkey / sign / verify/25-3 随机源纪律:内核 CSPRNG 与 k 复用灾难。
入门版到这里就够了;进阶版会多出:真实的国密签名源码(Z_A 参与哈希计算那一段)、genkey/sign/verify命令的实测闭环记录,以及"改一个身份字符就验签失败"的对拍实验。
想动手 → 仓库https://gitee.com/pei-xiaoguang/kestrel-llm,从 Day 1 开始。
生词备注:CSPRNG= 操作系统内核提供的"密码学安全随机数来源"(Cryptographically Secure Pseudo-Random Number Generator);签名每次都从它取一个新的随机数。
下篇预告:一次签名只是一个零件。第 26 篇把它装成一条链——从发布方的私钥,到文件里的签名,再到设备内置的公钥,任何一处验不过就拒绝加载。