4、常用签名算法:RSA与ECDSA

签名算法是整个日志防篡改链条的“锁”。锁不行,链再长也白搭。 目前工业界用得最多的就是 RSA 和 ECDSA。 我个人习惯先看业务场景再选算法,毕竟这两东西差别挺大。

4.1 RSA:老牌可靠,但有点“胖”

RSA 诞生于 1977 年,数学基础是“大整数分解困难”。 签名时用私钥加密摘要,验签时用公钥解密比对。 我最早做 PKI 项目时用的就是 RSA 2048,稳定,但心里总想着它那又长又慢的签名生成。

核心参数:密钥长度通常选 2048 或 4096 位。安全强度对标对称加密 112 位和 128 位。

RSA 的签名过程其实很直接:

  • 对消息做哈希(比如 SHA-256)得到摘要
  • 用私钥对摘要做 RSA 加密(实际是模幂运算)得到签名
  • 验签时用公钥解密,比对哈希值

避坑:我曾经遇到一个项目,日志签名用 RSA 1024,结果 3 年后被证明不安全,全部证书得换。现在起码要 2048 起步,别省那点计算资源。

4.2 ECDSA:短小精悍,但曲线选择要当心

ECDSA 基于椭圆曲线离散对数难题。同样的安全强度,密钥长度只有 RSA 的十分之一。 你想想看,2048 位 RSA 和 256 位 ECDSA 安全等级差不多,但 ECDSA 的签名长度才 64 字节,RSA 要 256 字节。 在日志链式存储里,每个区块都要嵌入签名,ECDSA 能让链长得更长、更省空间。

ECDSA 签名过程比 RSA 复杂一点,涉及随机数 k:

  1. 计算消息的哈希 z
  2. 选取随机数 k,计算椭圆曲线点 R = k * G,取 x 坐标 r
  3. 计算 s = k⁻¹ (z + r * d) mod n,d 是私钥
  4. 签名对为 (r, s)

提示:随机数 k 不能用两次,也不能用固定值。否则私钥当场泄露。我见过一位同事把 k 写死在代码里,测试网爆了后直接被提取私钥…

4.3 核心对比:选谁更合适?

对比维度 RSA ECDSA
密钥长度(安全128位) 3072 位 256 位
签名长度 ≈ 256 字节 ≈ 64 字节
签名速度 慢(模幂运算) 快(标量乘法)
验签速度 较慢 稍快
抗量子攻击 弱(Shor算法) 弱(Shor算法)
标准支持 PKCS#1 v1.5 / PSS FIPS 186-4,SECG

简单结论:日志链式存储这种签名密集的场景,我倾向用 ECDSA。 体积小、速度快,256 位已经够用。除非你对接的老系统只认 RSA,那就用 RSA-PSS(更安全,避免 PKCS#1 v1.5 的漏洞)。

4.4 代码示例:Go 语言实现签名与验签

我平时用 Go 写日志链组件,下面给两个核心函数,你感受下差异:

// RSA 签名(使用 PSS)
func RSASign(msg []byte, priv *rsa.PrivateKey) ([]byte, error) {
    hashed := sha256.Sum256(msg)
    return rsa.SignPSS(rand.Reader, priv, crypto.SHA256, hashed[:], nil)
}

// ECDSA 签名(P256 曲线)
func ECDSASign(msg []byte, priv *ecdsa.PrivateKey) ([]byte, error) {
    hashed := sha256.Sum256(msg)
    r, s, _ := ecdsa.Sign(rand.Reader, priv, hashed[:])
    // 将 (r,s) 序列化为 ASN.1 或固定 64 字节
    signature := append(r.Bytes(), s.Bytes()...)
    return signature, nil
}

注意 ECDSA 验签时要检查 (r,s) 是否在合法范围内,并且 公钥曲线必须和签名曲线一致。 我遇到过有人脱机导入公钥时误用了 secp256k1 的曲线,结果验签全失败。

4.5 知识图谱:RSA vs ECDSA 签名流程

RSA 与 ECDSA 签名验证流程对比 RSA 签名 私钥对摘要做模幂运算 → 签名 RSA 验签 公钥解密签名 → 比对哈希 ECDSA 签名 选择随机数k → 计算(r,s)对 ECDSA 验签 利用公钥验证 (r,s) 有效 关键差异 • RSA 签名长度固定,ECDSA 签名长度可压缩(64 字节 vs 256 字节) • RSA 对密钥长度敏感,ECDSA 对随机数质量敏感 建议:新系统优先选 ECDSA(P-256),兼容性需求选 RSA-PSS

4.6 总结:实际选型的几个铁律

  • 性能优先:日志链每秒可能有成千上万次签名 → 选 ECDSA(P-256 或 P-384)
  • 兼容优先:要和各种 CA、HSM 互通 → 选 RSA 2048(PSS 填充)
  • 安全底线:两个算法都不要用 1024 位或 secp160k1 等弱曲线
  • 防重放:签名消息里务必带上时间戳或区块计数器

我自己的经验:做日志链存储,一开始就定好曲线和填充方式,后期改算法代价极大。不如先花半天测试两种算法在目标 CPU 上的签名吞吐,再决定。