4、ECDSA 签名算法:椭圆曲线密码学基础、ECDSA 签名与验签、曲线选择(P-256 / secp256k1)
ECDSA,全称是椭圆曲线数字签名算法。说白了,它就是 ECC(椭圆曲线密码学)和 DSA(数字签名算法)的结合体。为什么我们要专门花一章来讲它?因为现在绝大多数的组件签名、区块链钱包、TLS 证书,底层用的都是它。
我记得刚接触这个算法时,第一反应是「这数学也太复杂了吧」。但后来我发现,作为工程师,你不需要把每条公式都推导一遍。你只需要理解它的核心逻辑、知道怎么选曲线、能看懂签名和验签的过程,就足够了。
椭圆曲线密码学基础
先聊聊椭圆曲线。它长什么样?数学上,它满足这样一个方程:
y² = x³ + ax + b
嗯,就这么简单。画出来是一条关于 x 轴对称的曲线。但真正用在密码学里的,不是实数域上的连续曲线,而是有限域上的离散点集。
我个人习惯把椭圆曲线密码学理解为「在一个封闭的沙盒里做加法」。你可以在曲线上取两个点,做点加法,得到第三个点。反复做,就能得到一个很大的循环群。这个群里的元素数量,就是曲线的阶。
为什么它能做密码?因为「已知起点 G 和终点 Q,求做了多少次点加」——这个问题,目前没有多项式时间的解法。这就是椭圆曲线离散对数问题(ECDLP)。
核心概念速览:
- 基点 G:公开的曲线上的一个点,所有运算的起点。
- 私钥 d:一个随机的大整数,必须保密。
- 公钥 Q:Q = d * G,即私钥乘以基点。公开。
- 阶 n:使得 n * G = 无穷远点的最小正整数。决定了密钥空间的大小。
我在项目中遇到过一个问题:有人把私钥直接存成字符串,结果被反编译拿到了。你想想看,私钥一旦泄露,整个签名体系就形同虚设。所以,私钥的存储一定要用硬件安全模块(HSM)或安全飞地(Secure Enclave)。
ECDSA 签名与验签
ECDSA 的签名过程,其实比 RSA 要复杂一点。它引入了随机数 k,每次签名都不一样。这意味着,同样的消息,每次签名出来的值都不同。这是好事,能防止重放攻击。
签名过程大致分四步:
- 计算消息哈希:对消息 m 做哈希,得到 e = hash(m)。
- 生成随机数 k:在 [1, n-1] 范围内选一个随机数 k。注意,这个 k 必须每次都不一样,且绝对不能泄露。
- 计算点 R = k * G:取 R 的 x 坐标作为 r。如果 r 为 0,重新选 k。
- 计算 s = k⁻¹ * (e + r * d) mod n:如果 s 为 0,重新选 k。
最终签名就是 (r, s) 对。长度取决于曲线参数,P-256 下是 64 字节。
⚠️ 我曾经踩过一个坑:随机数 k 的生成必须使用密码学安全的随机数生成器(CSPRNG)。如果 k 有规律,或者两次签名用了同一个 k,私钥就能被直接算出来。索尼 PS3 的签名密钥泄露,就是这个原因。别用 rand(),别用时间戳,别用 UUID。
验签过程相对简单,不需要私钥:
- 检查 r 和 s 是否在有效范围内。
- 计算 e = hash(m)。
- 计算 w = s⁻¹ mod n。
- 计算 u1 = e * w mod n,u2 = r * w mod n。
- 计算点 P = u1 * G + u2 * Q。
- 如果 P 的 x 坐标等于 r,验签通过。
这里有个细节:验签时用的是公钥 Q,不是私钥。所以任何人都可以验证签名的有效性。这也是数字签名的核心价值——公开可验证。
💡 我的建议:在实际开发中,不要自己实现 ECDSA。用现成的库,比如 OpenSSL、libsecp256k1、Bouncy Castle。这些库经过了大量安全审计,比你自己写的要靠谱得多。
曲线选择:P-256 vs secp256k1
曲线选择是个大问题。目前最主流的两条曲线是 P-256(也叫 prime256v1)和 secp256k1。它们都是 256 位曲线,安全强度相当,但设计哲学完全不同。
| 特性 | P-256 (NIST P-256) | secp256k1 |
|---|---|---|
| 参数来源 | NIST 标准,参数由随机种子生成 | Koblitz 曲线,参数最小化 |
| 性能 | 中等,有优化空间 | 较快,尤其适合硬件实现 |
| 安全性 | 广泛认可,经过多年分析 | 同样安全,但部分人担心 NIST 曲线有后门 |
| 主要应用 | TLS 证书、政府标准、iOS/macOS 签名 | 比特币、以太坊、区块链生态 |
| 曲线方程参数 a | -3 | 0 |
| 曲线方程参数 b | 0x5ac635d8...(随机生成) | 7 |
我个人习惯这样选:
- 如果你的应用需要和浏览器、操作系统、TLS 协议交互,选 P-256。它兼容性最好,几乎所有平台都原生支持。
- 如果你的应用在区块链或加密货币领域,选 secp256k1。它是比特币和以太坊的标配,生态工具链最成熟。
- 如果你在做组件签名,比如给移动 App 或桌面软件签名,我建议优先考虑 P-256。因为 Apple 和 Microsoft 的签名工具链对 P-256 支持得最好。
避坑指南:不要为了「看起来更安全」而选择 384 位或 521 位曲线。256 位曲线的安全强度已经足够(相当于 3072 位 RSA)。更长的曲线只会带来性能开销,而且兼容性会变差。我在项目中见过有人用 P-521,结果在老旧设备上验签耗时超过 1 秒——完全没必要。
ECDSA 核心流程
下面这张图,把 ECDSA 的签名和验签流程串起来了。你可以看到,私钥只出现在签名端,公钥只出现在验签端。中间的消息哈希和曲线运算是桥梁。
代码示例:使用 OpenSSL 进行 ECDSA 签名
光说不练假把式。下面我用 OpenSSL 的命令行工具,演示一下 P-256 曲线的签名和验签。你可以在自己的机器上跑一遍。
# 1. 生成 P-256 私钥
openssl ecparam -genkey -name prime256v1 -out private.pem
# 2. 从私钥导出公钥
openssl ec -in private.pem -pubout -out public.pem
# 3. 对消息文件签名
echo "hello, ecdsa" > message.txt
openssl dgst -sha256 -sign private.pem -out signature.bin message.txt
# 4. 验签
openssl dgst -sha256 -verify public.pem -signature signature.bin message.txt
# 输出: Verified OK
如果你用的是 secp256k1,只需要把 prime256v1 换成 secp256k1 即可。OpenSSL 1.1.1 以上版本原生支持 secp256k1。
💡 小技巧:签名文件 signature.bin 是 DER 格式的。你可以用 openssl asn1parse -inform DER -in signature.bin 查看它的内部结构,能看到 r 和 s 两个大整数。
总结
ECDSA 是目前最主流的签名算法之一。它的核心优势是:密钥短、性能好、安全性高。256 位的 ECDSA 密钥,安全强度相当于 3072 位的 RSA 密钥。
曲线选择上,P-256 适合通用场景和合规场景,secp256k1 适合区块链场景。我个人更倾向于 P-256,因为它的生态支持更广泛,而且 NIST 标准在政府和企业级应用中几乎是必选项。
最后再强调一次:随机数 k 的安全性是 ECDSA 的生命线。别在这上面偷懒。用 CSPRNG,用硬件随机数,别自己造轮子。