4、签名算法详解:RSA签名、ECDSA签名、Ed25519签名对比与选型

签名算法是固件防篡改的「最后一道锁」。选错了,后面全白搭。

我见过不少项目,芯片性能明明够用,却因为选了个不合适的签名算法,导致启动时间多了好几秒,或者签名验证代码把 Flash 空间吃光了。今天咱们就把 RSA、ECDSA、Ed25519 这三兄弟掰开揉碎讲清楚。

4.1 RSA 签名:老牌劲旅,但有点「重」

RSA 诞生于 1977 年,至今还在大量使用。它的安全性基于大整数分解难题——给你两个大质数相乘的结果,想反推出原来的质数,非常困难。

核心特点:

  • 签名长度固定:256 字节(2048 位密钥)或 512 字节(4096 位密钥)
  • 验签速度比签名快很多
  • 密钥对生成非常慢,尤其是大密钥

我在一个 IoT 网关项目里用过 RSA-2048。当时芯片主频只有 200 MHz,验签一次花了将近 80 毫秒。嗯,这个时间对冷启动来说还能接受,但如果你要做 OTA 逐包验证,那就有点悬了。

避坑指南:我曾经踩过一个坑——RSA 签名必须使用 PKCS#1 v1.5 或 PSS 填充模式。有人图省事直接对哈希值做裸指数运算,结果被选择明文攻击打穿了。记住:永远不要用「教科书 RSA」。

4.2 ECDSA 签名:椭圆曲线上的「轻骑兵」

ECDSA 用椭圆曲线代替了大整数模幂运算。同样的安全强度下,密钥长度短得多。比如 256 位 ECDSA 密钥的安全等级 ≈ 3072 位 RSA 密钥。

对比项 RSA-2048 ECDSA P-256
密钥长度 2048 位 256 位
签名长度 256 字节 64 字节
验签速度 较慢 较快
密钥生成速度 极慢 快

ECDSA 的签名只有 64 字节,比 RSA 的 256 字节小得多。这在固件包体积受限的场景下非常有用——比如 BLE 广播包或者窄带物联网。

但 ECDSA 有个「脾气」:它需要高质量的随机数。如果随机数生成器出了问题,私钥就可能被恢复。2010 年索尼 PS3 的签名密钥泄露,就是因为随机数重复使用。我建议你在嵌入式平台上一定要给 ECDSA 配一个硬件 TRNG,别用软件伪随机。

4.3 Ed25519 签名:后起之秀,又快又安全

Ed25519 是 Edwards 曲线数字签名算法,属于 Curve25519 家族。它设计时就考虑了嵌入式场景,很多「坑」在算法层面就填平了。

我个人习惯:新项目只要条件允许,我会优先选 Ed25519。为什么?因为它没有分支、没有数组索引依赖数据、没有秘密条件——这些特性天然抵抗侧信道攻击。你想想看,在嵌入式设备上做侧信道防护有多麻烦?Ed25519 直接帮你省了这部分工作量。

Ed25519 的签名也是 64 字节,和 ECDSA 一样。但它的验签速度比 ECDSA 快 2-3 倍,而且不需要随机数生成器。密钥生成也快,几乎瞬间完成。

特性 Ed25519 ECDSA P-256 RSA-2048
抗侧信道 天生 需额外防护 需额外防护
随机数需求 无 必须高质量 签名时需要
验签速度 极快 快 慢
代码体积 小 中等 大

4.4 选型决策:到底用哪个?

这个问题没有标准答案,但我可以给你一个实用的决策流程。

签名算法选型决策流程 开始选型 芯片有硬件加速器? 是 用硬件支持的算法 否 软件实现 需要兼容旧系统? 是 选 RSA 否 选 Ed25519 确定签名算法 说明 硬件加速器优先用硬件支持的算法 无硬件加速时,新项目首选 Ed25519

具体来说,我的建议是这样的:

  • 新项目,无兼容包袱:直接上 Ed25519。代码量小、速度快、抗侧信道,省心。
  • 需要兼容现有 PKI 体系:比如对接已有 CA 证书链,那就用 RSA。很多企业 PKI 还是 RSA 的天下。
  • 芯片有 ECC 硬件加速器:比如某些 ARM Cortex-M 系列内置了椭圆曲线加速指令,那用 ECDSA P-256 也很香。
  • 极端资源受限:比如 8 位 MCU、几 KB RAM,Ed25519 的代码体积优势就体现出来了。

我曾经犯过一个错:在一个电池供电的设备上选了 RSA-4096。验签一次 300 毫秒,电流飙到 50 mA。本来设备待机功耗只有 10 µA,结果每次 OTA 验证都把电池耗掉一大截。后来换成 Ed25519,验签时间降到 15 毫秒,功耗问题迎刃而解。

4.5 代码示例:三种算法的签名与验签

下面我用 Python 的 cryptography 库演示三种算法的基本用法。实际嵌入式开发中你会用 C 语言调用 mbedTLS 或 WolfSSL,但原理是一样的。

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, ec, ed25519, padding
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed

# 准备数据
message = b"Firmware image v2.1.0"

# ========== RSA 签名 ==========
private_key_rsa = rsa.generate_private_key(public_exponent=65537, key_size=2048)
signature_rsa = private_key_rsa.sign(
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)
public_key_rsa = private_key_rsa.public_key()
public_key_rsa.verify(
    signature_rsa,
    message,
    padding.PSS(mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH),
    hashes.SHA256()
)
print(f"RSA 签名长度: {len(signature_rsa)} 字节")

# ========== ECDSA 签名 ==========
private_key_ec = ec.generate_private_key(ec.SECP256R1())
signature_ec = private_key_ec.sign(message, ec.ECDSA(hashes.SHA256()))
public_key_ec = private_key_ec.public_key()
public_key_ec.verify(signature_ec, message, ec.ECDSA(hashes.SHA256()))
print(f"ECDSA 签名长度: {len(signature_ec)} 字节")

# ========== Ed25519 签名 ==========
private_key_ed = ed25519.Ed25519PrivateKey.generate()
signature_ed = private_key_ed.sign(message)
public_key_ed = private_key_ed.public_key()
public_key_ed.verify(signature_ed, message)
print(f"Ed25519 签名长度: {len(signature_ed)} 字节")

你看,Ed25519 的 API 最简洁——不需要指定哈希算法、不需要填充模式。这就是设计上的「防呆」特性。

4.6 小结

三种算法各有适用场景。RSA 胜在生态成熟,ECDSA 胜在密钥短小,Ed25519 胜在安全与性能的平衡。我个人建议:没有历史包袱的新项目,优先考虑 Ed25519。它把很多容易出错的设计决策都替你做了,让你能更专注于固件本身的安全逻辑。