3. 签名验签流程拆解:签名生成、验签验证与核心计算

说实话,很多做软件的同学觉得签名验签就是个黑盒子——把数据扔进去,出来一堆乱码。但要做硬件加速,你必须把这个黑盒子拆成零件,看清楚每个齿轮怎么转的。

我个人习惯把签名验签拆成三个层面:签名生成流程、验签验证流程、以及藏在底层的核心计算环节。今天咱们就一层层剥开来看。

3.1 签名生成流程:从私钥到签名

先看签名端。假设你有一份数据要签名,手里有私钥。流程大概是这样的:

  1. 数据哈希:对原始消息做哈希运算,得到一个固定长度的摘要。
  2. 摘要编码:把哈希结果按照标准格式(比如PKCS#1 v1.5或PSS)填充编码。
  3. 私钥运算:用私钥对编码后的摘要做模幂运算(RSA)或点乘运算(ECDSA)。
  4. 输出签名:得到最终的签名值。

关键点:签名生成的核心是「私钥参与的计算」。私钥一旦泄露,签名就毫无意义。我在项目中见过有人把私钥直接写在代码里……嗯,那场面不太好看。

这里有个细节容易被忽略——哈希运算必须在签名之前。为什么?因为公钥算法处理长数据很慢,哈希能把任意长度的消息压缩成固定长度(比如256位),这样后面的模幂运算只需要处理几百到几千比特的数据。

3.2 验签验证流程:从签名到结果

验签是签名的逆过程,但用的密钥不同——公钥。流程如下:

  1. 数据哈希:对原始消息做同样的哈希运算。
  2. 公钥运算:用公钥对签名值做模幂运算(RSA)或点乘运算(ECDSA),得到解码后的摘要。
  3. 摘要比对:把解码后的摘要和本地计算的哈希结果做比对。
  4. 输出结果:一致则验签通过,否则失败。

避坑指南:我曾经在验签时忘记做哈希,直接把原始数据拿去和签名比对……结果当然是失败。后来排查了半天才发现,签名是对哈希做的,不是对原始数据做的。这个坑很多人踩过。

你想想看,验签其实就是在验证「这个签名是不是由对应的私钥生成的」。公钥能解开签名,说明签名一定是用私钥做的。这就是非对称加密的奇妙之处。

3.3 核心计算环节:模幂、点乘、哈希

好了,现在到了最核心的部分。签名验签的底层,其实就三个计算:模幂、点乘、哈希。这三个家伙占了整个流程90%以上的计算量。

3.3.1 模幂运算(RSA的核心)

模幂运算说白了就是计算 c = m^e mod n。看起来简单,但这里的指数 e 和模数 n 都是几百到几千比特的大数。直接算?计算机得算到天荒地老。

实际用的是模重复平方算法(也叫快速幂)。举个例子:

// 计算 m^e mod n
function modPow(m, e, n) {
    let result = 1;
    let base = m % n;
    while (e > 0) {
        if (e & 1) {           // 当前比特为1
            result = (result * base) % n;
        }
        base = (base * base) % n;  // 平方
        e = e >> 1;            // 右移一位
    }
    return result;
}

这个算法的复杂度是 O(log e),而不是 O(e)。对于2048位的RSA,e有2048位,log e就是2048次循环。每次循环做一次模乘和一次模平方。硬件加速的重点就是优化这些模乘操作。

注意:模幂运算中的模乘不能直接用普通乘法,因为两个2048位的数相乘会得到4096位的结果,普通CPU乘法器扛不住。硬件上一般用Montgomery模乘算法来避免大数除法。

3.3.2 点乘运算(ECDSA的核心)

椭圆曲线签名验签的核心是点乘:Q = k * G。这里的 k 是标量(私钥),G 是椭圆曲线上的基点,Q 是结果点。

点乘的实现和模幂很像,用的是双倍-加算法:

// 计算 k * G
function pointMul(k, G) {
    let result = O;  // 无穷远点
    let base = G;
    while (k > 0) {
        if (k & 1) {
            result = pointAdd(result, base);  // 点加
        }
        base = pointDouble(base);             // 点倍
        k = k >> 1;
    }
    return result;
}

每次点加和点倍都涉及到底层的模运算——模加、模减、模乘、模逆。其中模逆是最慢的,硬件上通常用扩展欧几里得算法或者费马小定理来加速。

我记得有一次做ECC加速器,发现点乘占了整个签名流程85%的时间。后来优化了点加和点倍的并行度,把延迟从2000个时钟周期降到了800个。效果立竿见影。

3.3.3 哈希运算

哈希运算相对简单,但也不能忽视。常用的哈希算法有SHA-256、SHA-384、SM3等。它们的核心是压缩函数——把固定长度的输入压缩成更短的输出。

以SHA-256为例,它的计算过程包括:

  • 消息填充:把消息补足到512位的整数倍。
  • 消息调度:从16个32位字扩展到64个32位字。
  • 64轮压缩:每轮做移位、异或、与、非等位运算。

哈希运算的特点是数据依赖少,非常适合硬件流水线。我在项目中做过一个SHA-256硬件模块,用8级流水线,吞吐量能达到3.2 Gbps。相比之下,软件实现通常只有几百Mbps。

3.4 三种核心计算的对比

为了让你看得更清楚,我把三种核心计算放在一起对比:

计算类型 所属算法 输入规模 计算复杂度 硬件加速难点
模幂 RSA 2048~4096位 O(log e) 大数模乘、Montgomery算法
点乘 ECDSA 256~512位 O(log k) 模逆、点加/点倍并行
哈希 SHA-256/SM3 512位分组 O(n) 流水线设计、位运算加速

从表里能看出来:模幂和点乘是计算密集型,哈希是数据密集型。硬件加速的重点应该放在前两个上,哈希用简单的流水线就能搞定。

3.5 整体流程的SVG框架图

下面这张图把签名和验签的完整流程画出来了,包括核心计算环节的位置:

签名验签完整流程与核心计算环节 签名生成流程 ① 消息哈希 (SHA-256) ② 摘要编码 (PKCS#1 PSS) ③ 私钥模幂/点乘 ④ 输出签名值 验签验证流程 ① 消息哈希 (SHA-256) ② 公钥模幂/点乘 ③ 摘要比对 ④ 通过/失败 核心计算环节(硬件加速重点) 模幂运算 RSA签名/验签 点乘运算 ECDSA签名/验签 哈希运算 SHA-256/SM3

从图上能看出来,签名和验签的流程是对称的——都从哈希开始,中间经过核心计算,最后输出结果。区别只在于:签名用私钥,验签用公钥。

3.6 小结

好了,这一章我们把签名验签的流程拆了个底朝天。你记住三件事就行:

  • 签名生成:哈希 → 编码 → 私钥运算 → 签名
  • 验签验证:哈希 → 公钥运算 → 比对 → 结果
  • 核心计算:模幂(RSA)、点乘(ECDSA)、哈希(SHA-256)