3、密码学基础(下):数字签名原理,RSA与ECDSA算法对比

好,咱们接着聊。上一节我们把哈希函数和对称加密讲透了,这一节轮到数字签名了。说白了,数字签名就是给固件盖个章,证明这东西确实是你写的,而且没人动过手脚。

我在做嵌入式安全那几年,见过太多因为签名没做好导致固件被篡改的案例。有的直接把私钥硬编码在代码里,有的签名算法选错了还在那沾沾自喜。嗯,今天咱们就把这事彻底捋清楚。

3.1 数字签名到底在解决什么问题?

你想想看,固件从开发到烧录,中间要经过多少人的手?编译服务器、测试环境、发布平台、OTA服务器……任何一个环节被渗透,固件都可能被替换成恶意版本。

数字签名要解决的就是两个核心问题:

  • 身份认证:这个固件到底是谁签发的?
  • 完整性校验:固件内容有没有被篡改过?

这两个问题,靠哈希函数只能解决一半——哈希能校验完整性,但没法证明是谁签的。数字签名把哈希和公钥密码结合起来,才真正把事办成了。

核心逻辑:私钥签名,公钥验签。私钥只有你一个人有,公钥可以公开给全世界。只要验签通过,就证明这个固件确实是用你的私钥签的,而且内容没变过。

3.2 数字签名的工作流程

我习惯把签名流程拆成三步,这样好记:

  1. 计算哈希:对固件二进制做一次哈希运算,得到固定长度的摘要。
  2. 签名:用私钥对哈希摘要进行加密,生成签名值。
  3. 打包:把固件原文 + 签名值 + 公钥证书(可选)一起发布出去。

验签流程反过来:

  1. 用公钥解密签名值,得到原始哈希摘要。
  2. 对收到的固件重新计算哈希。
  3. 两个哈希对比,一致则验签通过。

这里有个细节要注意——签名不是对整个固件加密,那太慢了。签名只对哈希摘要操作,效率高得多。

我的经验:实际项目中,签名值通常放在固件末尾,或者单独放在一个签名文件中。我见过有人把签名值放在固件头部,结果验签时把签名值自己也算了进去,导致永远验不过……这种坑,踩过一次就记住了。

3.3 RSA签名算法

RSA,全称Rivest-Shamir-Adleman,三个发明人的名字。这算法1983年就申请了专利,到现在还在广泛使用,生命力确实强。

RSA签名的核心原理基于大整数分解难题。给你两个大素数p和q,乘起来得到n很容易,但给你n让你分解出p和q,那就难了。私钥就是基于p和q生成的,公钥就是n加上一个指数e。

签名过程说白了就是:

签名值 = 哈希摘要 ^ d mod n   (d是私钥指数)

验签过程:

哈希摘要 = 签名值 ^ e mod n   (e是公钥指数)

看着简单吧?但实际工程里坑不少。

避坑指南:我曾经接手过一个项目,RSA签名用的密钥长度只有1024位。当时觉得够用了,结果没过两年就被学术界攻破了。现在行业标准是2048位起步,我建议直接上4096位。别嫌慢,安全第一。

3.4 ECDSA签名算法

ECDSA,全称Elliptic Curve Digital Signature Algorithm,基于椭圆曲线离散对数难题。说白了,就是椭圆曲线上点的加法运算,正向好算,逆向难求。

ECDSA的优势在哪?一个字——短。

同样安全强度下,ECDSA的密钥长度和签名长度都比RSA短得多。举个例子:

安全强度(位) RSA密钥长度 ECDSA密钥长度 签名长度
80 1024 160 320
112 2048 224 448
128 3072 256 512
192 7680 384 768
256 15360 521 1042

看到没?128位安全强度下,RSA要3072位密钥,ECDSA只要256位。这对嵌入式设备来说太重要了——存储空间、传输带宽、计算资源都省了一大截。

3.5 RSA vs ECDSA:实战对比

我在项目中两种算法都用过,说说我的真实感受:

对比维度 RSA ECDSA
签名速度 较慢(私钥操作) 较快
验签速度 较快(公钥操作) 较慢
密钥长度 长(2048+位) 短(256位)
签名长度 等于密钥长度 约2倍密钥长度
硬件支持 广泛 逐渐普及
抗量子攻击 弱 弱(两者都弱)

你可能会问,那到底选哪个?

我的建议是:

  • 如果设备资源紧张(比如IoT传感器、蓝牙设备),选ECDSA。密钥短、签名短,省空间。
  • 如果验签频率远高于签名频率(比如服务器验签),RSA的验签速度优势就体现出来了。
  • 如果硬件已经集成了RSA加速器,别折腾,直接用RSA。

个人习惯:我现在做新项目,默认选ECDSA with P-256曲线。原因很简单——NIST推荐、硬件支持好、性能均衡。除非客户明确要求RSA,否则我不换。

3.6 签名算法在固件中的实际应用

说了这么多理论,咱们看看实际代码长什么样。下面是一个用Python做ECDSA签名的示例:

from ecdsa import SigningKey, NIST256p
import hashlib

# 生成私钥
sk = SigningKey.generate(curve=NIST256p)
# 导出公钥
vk = sk.verifying_key

# 固件数据
firmware = b"Hello, this is my firmware binary..."

# 签名
signature = sk.sign(firmware, hashfunc=hashlib.sha256)

# 验签
try:
    vk.verify(signature, firmware, hashfunc=hashlib.sha256)
    print("验签通过!固件未被篡改。")
except:
    print("验签失败!固件可能被篡改。")

这段代码看着简单,但实际工程里要考虑的事多着呢:

  • 密钥怎么安全存储?不能硬编码。
  • 签名格式怎么定义?DER还是PEM?
  • 验签失败后怎么处理?是拒绝启动还是降级运行?

这些细节,咱们后面的章节会一个一个拆解。

3.7 知识体系总览

为了让你对整个数字签名体系有个全局认识,我画了张图:

数字签名知识体系结构图 数字签名知识体系结构 数字签名 身份认证 完整性校验 不可否认性 RSA 签名算法 ECDSA 签名算法 密钥长 / 验签快 硬件支持广泛 密钥短 / 签名短 适合嵌入式设备 选择建议:资源受限设备 → ECDSA 验签频繁场景 → RSA

这张图把数字签名的核心要素都串起来了。你从上往下看,先理解数字签名要解决什么问题,再看两种主流算法怎么选,最后落到实际应用场景。

3.8 小结

数字签名是固件安全的第一道防线。RSA和ECDSA各有千秋,没有绝对的好坏。关键是根据你的设备资源、性能需求和硬件支持来选。

我个人更倾向ECDSA,尤其是做IoT设备的时候。但如果你在做服务器端的固件验签服务,RSA的验签速度优势确实明显。

嗯,这一节就到这。记住一句话:私钥藏好,公钥公开,签名验签,缺一不可。