4. 数字签名算法:RSA签名、ECDSA签名、Ed25519签名、SM2签名

数字签名是安全更新的「身份证」。没有它,你下载的固件包可能是攻击者塞给你的恶意代码。我这些年见过太多因为签名选型翻车的案例——有的图省事用弱算法,有的混用签名模式导致验证失败。今天咱们就把四种主流签名算法掰开揉碎讲清楚。

4.1 RSA签名:老牌劲旅,但要注意「坑」

RSA 签名应该是大家最熟悉的。它的核心逻辑是:私钥签名,公钥验签。我最早做 OTA 更新时用的就是 RSA-2048,那时候觉得这玩意儿稳如老狗。后来踩了坑才明白,RSA 的「安全水位」跟参数选择强相关。

关键参数速查表

密钥长度安全等级签名长度推荐场景
1024 位已不安全128 字节❌ 禁止新用
2048 位当前安全256 字节✅ 通用场景
4096 位长期安全512 字节✅ 高安全场景

RSA 签名有两种主流填充模式:PKCS#1 v1.5 和 PSS。我个人强烈建议用 PSS。为什么?PKCS#1 v1.5 存在已知的 Bleichenbacher 攻击变种,虽然实际利用条件苛刻,但安全设计上没必要给自己留隐患。我在一个车机项目里就遇到过——第三方芯片的签名验签库只支持 PKCS#1 v1.5,我们硬是加了层 PSS 适配层才放心上线。

避坑指南

我曾经接手过一个遗留系统,RSA 签名用的 1024 位密钥 + PKCS#1 v1.5 填充。安全审计直接给了高危漏洞。后来花了两个月升级到 2048 位 + PSS,还顺带重构了密钥分发流程。记住:1024 位 RSA 在 2024 年的算力下已经可以被国家级对手暴力破解。

4.2 ECDSA签名:短密钥,高性能

ECDSA 基于椭圆曲线密码学。同样的安全强度下,它的密钥长度比 RSA 短得多——256 位 ECDSA 约等于 3072 位 RSA。这意味着签名更小、计算更快。我特别喜欢在 IoT 设备上用 ECDSA,因为那些 MCU 的算力和存储都抠得很。

但 ECDSA 有个「命门」:随机数质量。每次签名必须生成一个唯一的、不可预测的随机数 k。如果 k 重复或可预测,私钥就会直接泄露。这不是理论问题——索尼 PS3 的 ECDSA 签名就因为随机数生成器用了固定值,导致黑客直接算出了私钥,整个游戏生态被攻破。

我的经验

在嵌入式设备上实现 ECDSA 签名时,我建议用硬件真随机数发生器(TRNG)来生成 k。如果芯片没有 TRNG,至少要用 NIST SP 800-90A 批准的 DRBG,并且每次签名后清零内存中的 k 值。别问我为什么强调「清零」——我在一个工控项目里见过签名后 k 值残留在堆内存里被 dump 出来的案例。

常用的椭圆曲线有:

  • P-256(secp256r1):NIST 标准,兼容性最好,大多数平台原生支持
  • P-384(secp384r1):更高安全等级,适合长期有效证书
  • P-521(secp521r1):极高高安全场景,但计算开销大

4.3 Ed25519签名:现代、快速、防侧信道

Ed25519 是 Edwards 曲线数字签名算法,属于 EdDSA 家族。说实话,这是我目前最偏好的签名算法。为什么?因为它把 ECDSA 的那些「坑」全填平了。

首先,Ed25519 的签名是确定性的——它用私钥和消息哈希通过哈希函数生成随机数 k,而不是依赖外部随机数源。这意味着你不需要担心随机数重复的问题。其次,它的签名速度极快,在 ARM Cortex-M 级别的芯片上也能跑得飞起。第三,它的公钥和签名都很短:公钥 32 字节,签名 64 字节。

四种算法签名长度对比

算法公钥长度签名长度签名速度(相对)
RSA-2048256 字节256 字节慢
ECDSA P-25632 字节64 字节快
Ed2551932 字节64 字节非常快
SM232 字节64 字节快

Ed25519 还有一个隐藏优势:抗侧信道攻击。它的实现天然是常数时间的,不像 RSA 或 ECDSA 那样容易通过时间、功耗或电磁泄露来推断密钥。我在做金融级安全模块时,侧信道攻击是必须考虑的威胁模型,Ed25519 在这方面几乎是「开箱即用」的安全。

注意

Ed25519 虽然好,但兼容性是个问题。很多老旧系统或硬件安全模块(HSM)不支持 Ed25519。如果你要跟第三方系统对接,先确认对方验签库是否支持。我曾经在一个跨厂商的固件分发平台里,因为一家芯片厂商的 BootROM 只支持 RSA,不得不保留 RSA 作为 fallback 方案。

4.4 SM2签名:国标,必须懂

SM2 是中国国家密码管理局发布的椭圆曲线公钥密码算法标准(GM/T 0003-2012)。如果你做的是面向国内政府、金融、关键基础设施的项目,SM2 是强制要求。我参与过几个等保三级系统的设计,SM2 签名是必选项。

SM2 的曲线参数是固定的,使用 256 位素域上的一条特定椭圆曲线。它的签名算法流程跟 ECDSA 类似,但细节上有差异:

  • SM2 签名中使用了用户 ID(ZA 值)作为输入,将签名者身份绑定到签名中
  • SM2 的哈希函数固定使用 SM3(国密哈希算法)
  • SM2 签名验证时需要先计算 ZA 值,再计算待签名消息的哈希

说实话,SM2 的签名长度和 ECDSA P-256 一样(64 字节),安全强度也相当。但它的实现复杂度略高,主要是 ZA 值的计算和 SM3 哈希的配合。我在一个政务云项目里集成 SM2 时,发现很多开源库的 SM2 实现有边界条件 bug——比如 ZA 值中用户 ID 的长度编码方式搞错,导致签名验证失败。

实用建议

如果你需要同时支持国际算法和国密算法,可以考虑「双签名」策略:对同一份更新包分别用 RSA/ECDSA 和 SM2 签名,验签时根据设备类型选择对应的公钥验证。这样既满足合规要求,又不影响海外设备的兼容性。我在一个跨国车联网平台里就是这么设计的。

4.5 算法选型决策框架

说了这么多,到底该选哪个?我一般按这个逻辑来决策:

  1. 合规优先:如果项目有国密要求,直接选 SM2,没得商量
  2. 新项目、新硬件:优先 Ed25519,性能好、安全、省心
  3. 需要兼容老旧系统:RSA-2048 + PSS,虽然签名大但兼容性最好
  4. 算力受限的 IoT 设备:ECDSA P-256 或 Ed25519,看芯片是否支持硬件加速

一句话总结

RSA 是「老成持重但有点臃肿」,ECDSA 是「高效但需要小心伺候」,Ed25519 是「现代省心但生态不够广」,SM2 是「合规必选但实现要仔细」。没有银弹,只有最适合你场景的选择。

4.6 签名算法核心流程对比图

下面这张图展示了四种签名算法的核心流程对比。你可以看到,虽然底层数学不同,但「私钥签名、公钥验签」的宏观框架是一致的。

四种数字签名算法核心流程对比 四种数字签名算法核心流程对比 签名方(私钥) 验签方(公钥) 签名值 + 原文 RSA 签名 私钥加密哈希值 PKCS#1 v1.5 / PSS 填充 RSA 验签 公钥解密哈希值 对比哈希值 ECDSA 签名 生成随机数 k 计算 (r, s) 签名对 ECDSA 验签 验证 r, s 范围 椭圆曲线点乘验证 Ed25519 签名 确定性 k(哈希派生) Edwards 曲线点乘 Ed25519 验签 双标量乘验证 常数时间实现 SM2 签名 计算 ZA 值(含用户 ID) SM3 哈希 + 椭圆曲线签名 SM2 验签 重新计算 ZA 值 SM3 哈希 + 曲线验证 ⚠️ 所有算法都遵循「私钥签名、公钥验签」原则,但随机数生成、哈希函数、曲线参数等细节差异巨大 选型时务必考虑:合规要求、硬件支持、性能开销、侧信道防护能力

4.7 签名验证的工程实现要点

算法选好了,实现时还有几个容易翻车的地方:

  • 哈希算法必须跟签名算法匹配:RSA 通常用 SHA-256,SM2 必须用 SM3,Ed25519 内部用 SHA-512。混用会导致验签失败。
  • 签名值编码格式要统一:DER 编码还是原始字节?我见过因为签名格式不匹配导致跨平台验签失败的案例——PC 端用 DER,嵌入式端用原始字节,两边对不上。
  • 公钥分发要带证书链:裸公钥容易被替换。至少要用 X.509 证书包装,验签时验证证书链有效性。
  • 防重放攻击:签名本身不防重放。需要在签名内容里嵌入时间戳或序列号,验签时检查是否已使用过。

我的习惯

我在设计签名验证模块时,会先写一个「签名验证策略接口」,把算法选择、哈希算法、编码格式、证书验证全部抽象成可配置的策略。这样换算法时只需要新增一个策略实现,不用改业务逻辑。这个模式我在三个项目里复用过了,每次都很省心。