从比特币到 TLS:椭圆曲线如何守护数字世界的信任
01📖 目录
021. ECDSA 的前世今生
1.1 为什么需要 ECDSA?
2009 年 1 月 3 日,中本聪挖出比特币创世区块,其中使用的数字签名算法就是 ECDSA (Elliptic Curve Digital Signature Algorithm)。为什么选择 ECDSA 而不是传统的 RSA?场景:为一笔比特币交易生成数字签名
RSA-2048:
- 公钥大小:256 字节
- 私钥大小:256 字节
- 签名大小:256 字节
- 签名时间:~5ms (嵌入式设备)
ECDSA-256 (secp256k1):
- 公钥大小:33 字节 (压缩格式)
- 私钥大小:32 字节
- 签名大小:64-72 字节
- 签名时间:~2ms (嵌入式设备)
- 安全强度:等同于 RSA-3072
更短的密钥:256 位 ECC ≈ 3072 位 RSA 安全强度更快的速度:签名和验证速度是 RSA 的 2-3 倍1.2 ECDSA 的发展历程
| |
|---|
| 1985 | Neal Koblitz 和 Victor Miller 独立提出椭圆曲线密码学 (ECC) |
| 1992 | |
| 1999 | ANSI X9.62 标准发布,ECDSA 成为官方标准 |
| 2000 | |
| 2005 | NSA Suite B 密码套件选择 ECDSA 作为数字签名标准 |
| 2009 | 比特币使用 secp256k1 曲线的 ECDSA |
| 2013 | PlayStation 3 因 ECDSA 随机数重用漏洞被破解 |
| 2018 | |
| 2024 | mbedTLS 3.6.2 支持 14 种椭圆曲线 |
1.3 ECDSA 在现实世界的应用
比特币/以太坊:每笔交易都需要 ECDSA 签名验证2023 年数据:比特币网络每天处理约 30 万笔交易,全部使用 ECDSA# 查看 GitHub 的 TLS 证书
$ openssl s_client -connect github.com:443 | openssl x509 -text
Signature Algorithm: ecdsa-with-SHA384
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
ASN1 OID: prime256v1 # 即 secp256r1
智能门锁:使用 ECDSA 验证手机 APP 的开锁指令
032. 椭圆曲线数学基础
2.1 什么是椭圆曲线?
y² = x³ + ax + b (mod p)
其中 p 是大素数,4a³ + 27b² ≠ 0 (保证曲线光滑)。可视化示例 (实数域上的 secp256k1 曲线):y² = x³ +7
y
|
3| *
2| * *
1| * *
0+---*-------*----> x
-1| * *
-2| *
-3|
有限域上的椭圆曲线:
在密码学中,我们使用有限域 GF(p),曲线上的点是离散的:# secp256k1 参数
p = 2^256 - 2^32 - 977# 素数模
a = 0
b = 7
# 曲线方程:y² ≡ x³ + 7 (mod p)
2.2 椭圆曲线上的点运算
P + Q:过 P 和 Q 作直线,与曲线交于第三点 R’,R’ 关于 x 轴对称得 R = P + QP + P (倍点):过 P 作切线,与曲线交于 R’,对称得 R = 2P设 P = (x₁, y₁), Q = (x₂, y₂)
如果 P ≠ Q (点加法):
λ = (y₂ - y₁) / (x₂ - x₁) mod p
x₃ = λ² - x₁ - x₂ mod p
y₃ = λ(x₁ - x₃) - y₁ mod p
R = (x₃, y₃)
如果 P = Q (倍点运算):
λ = (3x₁² + a) / (2y₁) mod p
x₃ = λ² - 2x₁ mod p
y₃ = λ(x₁ - x₃) - y₁ mod p
R = (x₃, y₃)
k·P = P + P + ... + P (k 次)
这是 ECDSA 的核心运算,使用双倍-加法算法优化:defscalar_mult(k, P):
"""计算 k·P"""
result = O # 无穷远点
addend = P
while k:
if k & 1: # 如果 k 的最低位是 1
result = point_add(result, addend)
addend = point_double(addend) # 倍点
k >>= 1
return result
# 时间复杂度:O(log k),约 256 次倍点/加法运算
2.3 椭圆曲线的安全性
已知:P (基点), Q = k·P (公钥)
求解:k (私钥)
在椭圆曲线上,这个问题是计算困难的!
043. ECDSA 算法原理
3.1 密钥生成
d ← random(1, n-1) # 256 位随机数
// 生成 ECDSA 密钥对
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
// 使用 secp256r1 曲线
mbedtls_ecdsa_genkey(&ctx, MBEDTLS_ECP_DP_SECP256R1,
mbedtls_ctr_drbg_random, &ctr_drbg);
// ctx.d 是私钥 (256 位整数)
// ctx.Q 是公钥 (椭圆曲线上的点)
3.2 签名生成 (Sign)
1. 计算消息哈希:
e = H(m) # 例如 SHA-256(m)
2. 截断哈希 (如果需要):
如果 len(e) > len(n),则截断 e 的高位
3. 生成随机数 k:
k ← random(1, n-1) # 每次签名必须不同!
4. 计算临时公钥:
(x, y) = k·G
r = x modn
如果 r = 0,返回步骤 3
5. 计算签名的第二部分:
s = k⁻¹(e + r·d) modn
如果 s = 0,返回步骤 3
6. 返回签名 (r, s)
k 泄露或重用会导致私钥泄露!(PlayStation 3 漏洞)确定性签名 (RFC 6979):
为避免随机数问题,使用 HMAC-DRBG 生成确定性的 k:k = HMAC-DRBG(私钥 d, 消息哈希 e)
mbedTLS 默认使用确定性签名 (MBEDTLS_ECDSA_DETERMINISTIC)。3.3 签名验证 (Verify)
1. 验证 r, s 在范围内:
检查 1 ≤ r < n 且 1 ≤ s < n
2. 计算消息哈希:
e = H(m)
3. 计算辅助值:
w = s⁻¹ modn
u₁ = e·w modn
u₂ = r·w modn
4. 计算验证点:
(x, y) = u₁·G + u₂·Q # 双标量乘法
5. 验证:
如果 (x, y) = O (无穷远点),返回失败
v = x modn
如果 v = r,返回成功,否则失败
为什么验证有效?
签名时:s = k⁻¹(e + r·d) mod n
推导: k = s⁻¹(e + r·d) mod n
k·G = s⁻¹(e + r·d)·G
k·G = s⁻¹·e·G + s⁻¹·r·d·G
k·G = u₁·G + u₂·Q (因为 Q = d·G)
而 r = (k·G).x,所以验证点的 x 坐标应该等于 r
3.4 签名格式
签名 = r || s (64 字节)
DER 编码 (ASN.1 格式,TLS/X.509 使用):SEQUENCE {
r INTEGER,
s INTEGER
}
示例 (70 字节):
30440220[32字节r]0220[32字节s]
[长度]30[序列长度]02[r长度][r]02[s长度][s]
054. mbedTLS 源码深度剖析
4.1 核心数据结构
// ECDSA 上下文就是 ECC 密钥对
typedef mbedtls_ecp_keypair mbedtls_ecdsa_context;
// ECC 密钥对结构
typedefstructmbedtls_ecp_keypair {
mbedtls_ecp_group grp; // 椭圆曲线参数
mbedtls_mpi d; // 私钥 (大整数)
mbedtls_ecp_point Q; // 公钥 (曲线上的点)
} mbedtls_ecp_keypair;
typedefstructmbedtls_ecp_group {
mbedtls_ecp_group_id id; // 曲线 ID (如 SECP256R1)
mbedtls_mpi P; // 素数模
mbedtls_mpi A; // 曲线参数 a
mbedtls_mpi B; // 曲线参数 b
mbedtls_ecp_point G; // 基点 (生成元)
mbedtls_mpi N; // 基点的阶
size_t pbits; // P 的位数
size_t nbits; // N 的位数
// ... 优化相关字段
} mbedtls_ecp_group;
// Jacobian 坐标系:(X, Y, Z) 表示仿射坐标 (X/Z², Y/Z³)
typedefstructmbedtls_ecp_point {
mbedtls_mpi X; // X 坐标
mbedtls_mpi Y; // Y 坐标
mbedtls_mpi Z; // Z 坐标 (Z=0 表示无穷远点, Z=1 表示仿射坐标)
} mbedtls_ecp_point;
4.2 签名生成源码分析
主函数 (ecdsa.c:mbedtls_ecdsa_sign_restartable):intmbedtls_ecdsa_sign_restartable(
mbedtls_ecp_group *grp,
mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, // 私钥
constunsignedchar *buf, // 消息哈希
size_t blen,
int (*f_rng)(void *, unsignedchar *, size_t),
void *p_rng,
int (*f_rng_blind)(void *, unsignedchar *, size_t),
void *p_rng_blind,
mbedtls_ecdsa_restart_ctx *rs_ctx)
{
int ret, key_tries, sign_tries;
mbedtls_ecp_point R;
mbedtls_mpi k, e, t;
// 1. 检查曲线是否支持 ECDSA
if (!mbedtls_ecdsa_can_do(grp->id) || grp->N.p == NULL) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
// 2. 检查私钥范围:1 ≤ d < n
if (mbedtls_mpi_cmp_int(d, 1) < 0 ||
mbedtls_mpi_cmp_mpi(d, &grp->N) >= 0) {
return MBEDTLS_ERR_ECP_INVALID_KEY;
}
mbedtls_ecp_point_init(&R);
mbedtls_mpi_init(&k);
mbedtls_mpi_init(&e);
mbedtls_mpi_init(&t);
sign_tries = 0;
do {
if (sign_tries++ > 10) {
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
}
// 3. 生成临时密钥对 (k, k·G)
key_tries = 0;
do {
if (key_tries++ > 10) {
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
goto cleanup;
}
// 生成随机 k ∈ [1, n-1]
MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, &k,
f_rng, p_rng));
// 计算 R = k·G
MBEDTLS_MPI_CHK(mbedtls_ecp_mul_restartable(
grp, &R, &k, &grp->G,
f_rng_blind, p_rng_blind, ECDSA_RS_ECP));
// r = R.x mod n
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(r, &R.X, &grp->N));
} while (mbedtls_mpi_cmp_int(r, 0) == 0); // 确保 r ≠ 0
// 4. 从消息哈希派生 e
MBEDTLS_MPI_CHK(derive_mpi(grp, &e, buf, blen));
// 5. 生成盲化因子 t (防止时序攻击)
MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, &t,
f_rng_blind, p_rng_blind));
// 6. 计算 s = (e + r·d) / k mod n
// 使用盲化:s = t(e + rd) / (kt) mod n
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, r, d)); // s = r·d
MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&e, &e, s)); // e = e + r·d
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&e, &e, &t)); // e = t(e+rd)
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&k, &k, &t)); // k = kt
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&k, &k, &grp->N));
MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(s, &k, &grp->N)); // s = (kt)⁻¹
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(s, s, &e)); // s = t(e+rd)/(kt)
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(s, s, &grp->N));
} while (mbedtls_mpi_cmp_int(s, 0) == 0); // 确保 s ≠ 0
cleanup:
mbedtls_ecp_point_free(&R);
mbedtls_mpi_free(&k);
mbedtls_mpi_free(&e);
mbedtls_mpi_free(&t);
return ret;
}
staticintderive_mpi(const mbedtls_ecp_group *grp, mbedtls_mpi *x,
constunsignedchar *buf, size_t blen)
{
int ret;
size_t n_size = (grp->nbits + 7) / 8; // n 的字节数
size_t use_size = blen > n_size ? n_size : blen;
// 读取哈希的前 n_size 字节
MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(x, buf, use_size));
// 如果哈希位数 > n 的位数,右移截断
if (use_size * 8 > grp->nbits) {
MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(x, use_size * 8 - grp->nbits));
}
// 确保 x < n
if (mbedtls_mpi_cmp_mpi(x, &grp->N) >= 0) {
MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(x, x, &grp->N));
}
cleanup:
return ret;
}
mbedtls_ecp_gen_privkey() - 生成私钥:// 生成范围在 [1, N-1] 的随机数
intmbedtls_ecp_gen_privkey(mbedtls_ecp_group *grp,
mbedtls_mpi *d,
int (*f_rng)(void *, unsignedchar *, size_t),
void *p_rng)
{
size_t n_size = (grp->nbits + 7) / 8;
do {
// 生成 n_size 字节的随机数
MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(d, n_size, f_rng, p_rng));
// 确保 d < N
MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(d, 8 * n_size - grp->nbits));
} while (mbedtls_mpi_cmp_int(d, 1) < 0 || // d >= 1
mbedtls_mpi_cmp_mpi(d, &grp->N) >= 0); // d < N
return0;
}
4.3 签名验证源码分析
主函数 (ecdsa.c:mbedtls_ecdsa_verify_restartable):intmbedtls_ecdsa_verify_restartable(
mbedtls_ecp_group *grp,
constunsignedchar *buf, size_t blen, // 消息哈希
const mbedtls_ecp_point *Q, // 公钥
const mbedtls_mpi *r, // 签名 r
const mbedtls_mpi *s, // 签名 s
mbedtls_ecdsa_restart_ctx *rs_ctx)
{
int ret;
mbedtls_mpi e, s_inv, u1, u2;
mbedtls_ecp_point R;
mbedtls_ecp_point_init(&R);
mbedtls_mpi_init(&e);
mbedtls_mpi_init(&s_inv);
mbedtls_mpi_init(&u1);
mbedtls_mpi_init(&u2);
// 1. 验证 r, s 在范围 [1, n-1]
if (mbedtls_mpi_cmp_int(r, 1) < 0 ||
mbedtls_mpi_cmp_mpi(r, &grp->N) >= 0 ||
mbedtls_mpi_cmp_int(s, 1) < 0 ||
mbedtls_mpi_cmp_mpi(s, &grp->N) >= 0) {
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
goto cleanup;
}
// 2. 从消息哈希派生 e
MBEDTLS_MPI_CHK(derive_mpi(grp, &e, buf, blen));
// 3. 计算 w = s⁻¹ mod n
MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&s_inv, s, &grp->N));
// 4. 计算 u1 = e·w mod n, u2 = r·w mod n
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&u1, &e, &s_inv));
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&u1, &u1, &grp->N));
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&u2, r, &s_inv));
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&u2, &u2, &grp->N));
// 5. 计算 R = u1·G + u2·Q (双标量乘法)
MBEDTLS_MPI_CHK(mbedtls_ecp_muladd_restartable(
grp, &R, &u1, &grp->G, &u2, Q, ECDSA_RS_ECP));
// 6. 检查 R 是否为无穷远点
if (mbedtls_ecp_is_zero(&R)) {
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
goto cleanup;
}
// 7. 验证 R.x mod n == r
MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&R.X, &R.X, &grp->N));
if (mbedtls_mpi_cmp_mpi(&R.X, r) != 0) {
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
goto cleanup;
}
ret = 0; // 验证成功
cleanup:
mbedtls_ecp_point_free(&R);
mbedtls_mpi_free(&e);
mbedtls_mpi_free(&s_inv);
mbedtls_mpi_free(&u1);
mbedtls_mpi_free(&u2);
return ret;
}
性能关键:mbedtls_ecp_muladd_restartable() 实现双标量乘法优化。4.4 确定性签名 (RFC 6979)
如果两次签名使用相同的 k:
签名1:s₁ = k⁻¹(e₁ + r·d)
签名2:s₂ = k⁻¹(e₂ + r·d)
推导私钥:
s₁ - s₂ = k⁻¹(e₁ - e₂)
k = (e₁ - e₂) / (s₁ - s₂)
d = (s₁·k - e₁) / r
intmbedtls_ecdsa_sign_det_restartable(
mbedtls_ecp_group *grp,
mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d,
constunsignedchar *buf, size_t blen,
mbedtls_md_type_t md_alg, // 哈希算法
int (*f_rng_blind)(void *, unsignedchar *, size_t),
void *p_rng_blind,
mbedtls_ecdsa_restart_ctx *rs_ctx)
{
mbedtls_hmac_drbg_context rng_ctx;
unsignedchar data[2 * MBEDTLS_ECP_MAX_BYTES];
size_t grp_len = (grp->nbits + 7) / 8;
mbedtls_mpi h;
// 1. 准备 HMAC-DRBG 的种子:私钥 || 消息哈希
MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(d, data, grp_len));
MBEDTLS_MPI_CHK(derive_mpi(grp, &h, buf, blen));
MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&h, data + grp_len, grp_len));
// 2. 初始化 HMAC-DRBG
MBEDTLS_MPI_CHK(mbedtls_hmac_drbg_seed_buf(&rng_ctx, md_info,
data, 2 * grp_len));
// 3. 使用 HMAC-DRBG 作为随机数生成器进行签名
ret = mbedtls_ecdsa_sign_restartable(grp, r, s, d, buf, blen,
mbedtls_hmac_drbg_random, &rng_ctx,
f_rng_blind, p_rng_blind, rs_ctx);
mbedtls_hmac_drbg_free(&rng_ctx);
return ret;
}
4.5 可重启操作 (Restartable Operations)
问题:在嵌入式设备上,椭圆曲线运算可能需要数百毫秒,阻塞其他任务。解决方案:mbedTLS 支持将长时间运算分解为多个小步骤:// 设置最大操作数 (每次调用最多执行 10000 次基本运算)
mbedtls_ecp_set_max_ops(10000);
mbedtls_ecdsa_restart_ctx rs_ctx;
mbedtls_ecdsa_restart_init(&rs_ctx);
int ret;
do {
// 每次调用最多执行 10000 次运算,然后返回
ret = mbedtls_ecdsa_sign_restartable(
&grp, &r, &s, &d, hash, hash_len,
f_rng, p_rng, f_rng, p_rng, &rs_ctx);
if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) {
// 运算未完成,可以处理其他任务
handle_other_tasks();
}
} while (ret == MBEDTLS_ERR_ECP_IN_PROGRESS);
mbedtls_ecdsa_restart_free(&rs_ctx);
性能数据 (Cortex-M4 @ 168MHz):
065. 实战案例
5.1 案例 1:比特币交易签名
场景:为比特币交易生成 ECDSA 签名 (secp256k1 曲线)。#include"mbedtls/ecdsa.h"
#include"mbedtls/sha256.h"
#include"mbedtls/ctr_drbg.h"
#include"mbedtls/entropy.h"
// 比特币使用 secp256k1 曲线
#define BITCOIN_CURVE MBEDTLS_ECP_DP_SECP256K1
intbitcoin_sign_transaction(constunsignedchar *tx_data, size_t tx_len,
constunsignedchar *private_key_bytes,
unsignedchar *signature, size_t *sig_len)
{
int ret;
mbedtls_ecdsa_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
unsignedchar hash[32];
// 1. 初始化
mbedtls_ecdsa_init(&ctx);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
// 2. 初始化随机数生成器
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(constunsignedchar *)"bitcoin", 7);
if (ret != 0) goto cleanup;
// 3. 加载 secp256k1 曲线
ret = mbedtls_ecp_group_load(&ctx.grp, BITCOIN_CURVE);
if (ret != 0) goto cleanup;
// 4. 导入私钥 (32 字节)
ret = mbedtls_mpi_read_binary(&ctx.d, private_key_bytes, 32);
if (ret != 0) goto cleanup;
// 5. 计算公钥 Q = d·G
ret = mbedtls_ecp_mul(&ctx.grp, &ctx.Q, &ctx.d, &ctx.grp.G,
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) goto cleanup;
// 6. 计算交易的双重 SHA-256 哈希 (比特币标准)
unsignedchar hash1[32];
mbedtls_sha256(tx_data, tx_len, hash1, 0); // SHA-256
mbedtls_sha256(hash1, 32, hash, 0); // SHA-256 again
// 7. 生成 ECDSA 签名 (确定性,RFC 6979)
ret = mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256,
hash, 32,
signature, 256, sig_len,
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) goto cleanup;
printf("✅ 比特币交易签名成功!\n");
printf(" 签名长度: %zu 字节\n", *sig_len);
cleanup:
mbedtls_ecdsa_free(&ctx);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}
intbitcoin_verify_transaction(constunsignedchar *tx_data, size_t tx_len,
constunsignedchar *public_key_bytes,
size_t pubkey_len,
constunsignedchar *signature, size_t sig_len)
{
int ret;
mbedtls_ecdsa_context ctx;
unsignedchar hash[32];
mbedtls_ecdsa_init(&ctx);
// 1. 加载曲线
ret = mbedtls_ecp_group_load(&ctx.grp, BITCOIN_CURVE);
if (ret != 0) goto cleanup;
// 2. 导入公钥 (压缩格式 33 字节或未压缩 65 字节)
ret = mbedtls_ecp_point_read_binary(&ctx.grp, &ctx.Q,
public_key_bytes, pubkey_len);
if (ret != 0) goto cleanup;
// 3. 计算交易哈希
unsignedchar hash1[32];
mbedtls_sha256(tx_data, tx_len, hash1, 0);
mbedtls_sha256(hash1, 32, hash, 0);
// 4. 验证签名
ret = mbedtls_ecdsa_read_signature(&ctx, hash, 32, signature, sig_len);
if (ret == 0) {
printf("✅ 签名验证成功!交易有效。\n");
} else {
printf("❌ 签名验证失败!交易无效。\n");
}
cleanup:
mbedtls_ecdsa_free(&ctx);
return ret;
}
5.2 案例 2:TLS 证书签名验证
场景:验证 HTTPS 服务器的 ECDSA 证书。#include"mbedtls/x509_crt.h"
#include"mbedtls/ecdsa.h"
intverify_tls_certificate(constunsignedchar *cert_der, size_t cert_len)
{
int ret;
mbedtls_x509_crt cert;
mbedtls_x509_crt_init(&cert);
// 1. 解析 X.509 证书
ret = mbedtls_x509_crt_parse_der(&cert, cert_der, cert_len);
if (ret != 0) {
printf("❌ 证书解析失败: -0x%04x\n", -ret);
goto cleanup;
}
// 2. 检查签名算法
if (cert.sig_md == MBEDTLS_MD_SHA256 &&
cert.sig_pk == MBEDTLS_PK_ECDSA) {
printf("✅ 证书使用 ECDSA-SHA256 签名\n");
}
// 3. 获取公钥信息
mbedtls_ecp_keypair *ecp = mbedtls_pk_ec(cert.pk);
const mbedtls_ecp_curve_info *curve_info =
mbedtls_ecp_curve_info_from_grp_id(ecp->grp.id);
printf(" 曲线: %s (%d 位)\n",
curve_info->name, curve_info->bit_size);
// 4. 验证证书链 (需要 CA 证书)
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt_init(&ca_cert);
// 加载 CA 证书...
// ret = mbedtls_x509_crt_parse_file(&ca_cert, "ca.crt");
uint32_t flags;
ret = mbedtls_x509_crt_verify(&cert, &ca_cert, NULL, NULL,
&flags, NULL, NULL);
if (ret == 0) {
printf("✅ 证书验证成功!\n");
} else {
printf("❌ 证书验证失败: 0x%08x\n", flags);
}
mbedtls_x509_crt_free(&ca_cert);
cleanup:
mbedtls_x509_crt_free(&cert);
return ret;
}
5.3 案例 3:嵌入式设备固件签名
场景:IoT 设备验证固件更新的 ECDSA 签名。#include"mbedtls/ecdsa.h"
#include"mbedtls/sha256.h"
// 设备公钥 (烧录在 ROM 中)
staticconstunsignedchar DEVICE_PUBLIC_KEY[] = {
// secp256r1 公钥 (未压缩格式 65 字节)
0x04, // 未压缩标志
// X 坐标 (32 字节)
0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47,
// ... (省略)
// Y 坐标 (32 字节)
0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B,
// ... (省略)
};
typedefstruct {
uint32_t version;
uint32_t size;
uint8_t signature[72]; // DER 编码的 ECDSA 签名
uint8_t data[]; // 固件数据
} firmware_package_t;
intverify_firmware_signature(constfirmware_package_t *fw)
{
int ret;
mbedtls_ecdsa_context ctx;
unsignedchar hash[32];
mbedtls_ecdsa_init(&ctx);
// 1. 加载 secp256r1 曲线
ret = mbedtls_ecp_group_load(&ctx.grp, MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) goto cleanup;
// 2. 导入公钥
ret = mbedtls_ecp_point_read_binary(&ctx.grp, &ctx.Q,
DEVICE_PUBLIC_KEY,
sizeof(DEVICE_PUBLIC_KEY));
if (ret != 0) {
printf("❌ 公钥导入失败\n");
goto cleanup;
}
// 3. 计算固件哈希 (包括版本和大小)
mbedtls_sha256_context sha_ctx;
mbedtls_sha256_init(&sha_ctx);
mbedtls_sha256_starts(&sha_ctx, 0);
mbedtls_sha256_update(&sha_ctx, (uint8_t*)&fw->version, 4);
mbedtls_sha256_update(&sha_ctx, (uint8_t*)&fw->size, 4);
mbedtls_sha256_update(&sha_ctx, fw->data, fw->size);
mbedtls_sha256_finish(&sha_ctx, hash);
mbedtls_sha256_free(&sha_ctx);
// 4. 验证签名
ret = mbedtls_ecdsa_read_signature(&ctx, hash, 32,
fw->signature, 72);
if (ret == 0) {
printf("✅ 固件签名验证成功!可以安全更新。\n");
printf(" 版本: %u\n", fw->version);
printf(" 大小: %u 字节\n", fw->size);
} else {
printf("❌ 固件签名验证失败!拒绝更新。\n");
printf(" 错误码: -0x%04x\n", -ret);
}
cleanup:
mbedtls_ecdsa_free(&ctx);
return ret;
}
// 固件更新流程
voidfirmware_update_process(constuint8_t *package, size_t package_len)
{
firmware_package_t *fw = (firmware_package_t*)package;
// 1. 验证签名
if (verify_firmware_signature(fw) != 0) {
printf("⚠️ 签名验证失败,中止更新!\n");
return;
}
// 2. 擦除 Flash
printf("📝 擦除 Flash...\n");
flash_erase(FIRMWARE_START_ADDR, fw->size);
// 3. 写入新固件
printf("📝 写入固件...\n");
flash_write(FIRMWARE_START_ADDR, fw->data, fw->size);
// 4. 验证写入
printf("✅ 固件更新完成!\n");
// 5. 重启设备
system_reset();
}
5.4 案例 4:JWT 令牌签名 (ES256)
场景:使用 ECDSA 签名 JSON Web Token (JWT)。#include"mbedtls/ecdsa.h"
#include"mbedtls/base64.h"
#include"mbedtls/sha256.h"
#include<string.h>
// JWT Header: {"alg":"ES256","typ":"JWT"}
// JWT Payload: {"sub":"1234567890","name":"John Doe","iat":1516239022}
intcreate_jwt_es256(constchar *payload,
mbedtls_ecdsa_context *ctx,
char *jwt_out, size_t *jwt_len)
{
int ret;
unsignedchar hash[32];
unsignedchar signature[MBEDTLS_ECDSA_MAX_LEN];
size_t sig_len;
// JWT Header (ES256)
constchar *header = "{\"alg\":\"ES256\",\"typ\":\"JWT\"}";
// 1. Base64URL 编码 Header
unsignedchar header_b64[256];
size_t header_b64_len;
ret = mbedtls_base64_encode(header_b64, sizeof(header_b64),
&header_b64_len,
(constunsignedchar*)header, strlen(header));
if (ret != 0) return ret;
// 转换为 Base64URL (替换 +/= 为 -_)
base64_to_base64url(header_b64, header_b64_len);
// 2. Base64URL 编码 Payload
unsignedchar payload_b64[1024];
size_t payload_b64_len;
ret = mbedtls_base64_encode(payload_b64, sizeof(payload_b64),
&payload_b64_len,
(constunsignedchar*)payload, strlen(payload));
if (ret != 0) return ret;
base64_to_base64url(payload_b64, payload_b64_len);
// 3. 构造待签名数据: Header.Payload
char signing_input[2048];
snprintf(signing_input, sizeof(signing_input), "%s.%s",
header_b64, payload_b64);
// 4. 计算 SHA-256 哈希
mbedtls_sha256((constunsignedchar*)signing_input,
strlen(signing_input), hash, 0);
// 5. ECDSA 签名
ret = mbedtls_ecdsa_write_signature(ctx, MBEDTLS_MD_SHA256,
hash, 32,
signature, sizeof(signature), &sig_len,
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) return ret;
// 6. Base64URL 编码签名
unsignedchar sig_b64[256];
size_t sig_b64_len;
ret = mbedtls_base64_encode(sig_b64, sizeof(sig_b64), &sig_b64_len,
signature, sig_len);
if (ret != 0) return ret;
base64_to_base64url(sig_b64, sig_b64_len);
// 7. 构造完整 JWT: Header.Payload.Signature
snprintf(jwt_out, *jwt_len, "%s.%s.%s",
header_b64, payload_b64, sig_b64);
*jwt_len = strlen(jwt_out);
printf("✅ JWT 生成成功!\n");
printf(" %s\n", jwt_out);
return0;
}
// 验证 JWT
intverify_jwt_es256(constchar *jwt, mbedtls_ecdsa_context *ctx)
{
int ret;
char jwt_copy[2048];
strcpy(jwt_copy, jwt);
// 1. 分割 JWT: Header.Payload.Signature
char *header = strtok(jwt_copy, ".");
char *payload = strtok(NULL, ".");
char *signature_b64 = strtok(NULL, ".");
if (!header || !payload || !signature_b64) {
return-1;
}
// 2. 重构待验证数据
char signing_input[2048];
snprintf(signing_input, sizeof(signing_input), "%s.%s", header, payload);
// 3. 计算哈希
unsignedchar hash[32];
mbedtls_sha256((constunsignedchar*)signing_input,
strlen(signing_input), hash, 0);
// 4. 解码签名
unsignedchar signature[MBEDTLS_ECDSA_MAX_LEN];
size_t sig_len;
base64url_to_base64((unsignedchar*)signature_b64, strlen(signature_b64));
ret = mbedtls_base64_decode(signature, sizeof(signature), &sig_len,
(constunsignedchar*)signature_b64,
strlen(signature_b64));
if (ret != 0) return ret;
// 5. 验证签名
ret = mbedtls_ecdsa_read_signature(ctx, hash, 32, signature, sig_len);
if (ret == 0) {
printf("✅ JWT 验证成功!\n");
} else {
printf("❌ JWT 验证失败!\n");
}
return ret;
}
076. 常用椭圆曲线详解
6.1 NIST 标准曲线
mbedTLS 支持的 NIST P-曲线 (FIPS 186-4): | | | | |
|---|
| P-192 | | | | MBEDTLS_ECP_DP_SECP192R1 |
| P-224 | | | | MBEDTLS_ECP_DP_SECP224R1 |
| P-256 | | | | MBEDTLS_ECP_DP_SECP256R1 |
| P-384 | | | | MBEDTLS_ECP_DP_SECP384R1 |
| P-521 | | | | MBEDTLS_ECP_DP_SECP521R1 |
P-256 (secp256r1 / prime256v1) - 最常用:// 曲线参数
p = 2^256 - 2^224 + 2^192 + 2^96 - 1
a = -3
b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B
G = (0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,
0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5)
n = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
// 使用示例
mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256R1);
性能对比 (Cortex-M4 @ 168MHz):6.2 Koblitz 曲线 (secp256k1)
// 曲线参数
p = 2^256 - 2^32 - 977
a = 0
b = 7// 简单!
G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8)
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
// 使用示例
mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP256K1);
无后门疑虑:参数来源透明 (NIST 曲线被质疑可能有 NSA 后门)secp256k1 vs secp256r1 性能:操作 secp256k1 secp256r1
密钥生成 75ms85ms
签名 80ms90ms
验证 155ms175ms
6.3 Brainpool 曲线
欧洲标准曲线 (RFC 5639),参数生成过程完全透明: | | |
|---|
| brainpoolP256r1 | | MBEDTLS_ECP_DP_BP256R1 |
| brainpoolP384r1 | | MBEDTLS_ECP_DP_BP384R1 |
| brainpoolP512r1 | | MBEDTLS_ECP_DP_BP512R1 |
6.4 曲线选择建议
✅ 首选:P-256 (secp256r1)
- 广泛支持
- 性能良好
- TLS 1.3 强制要求
✅ 备选:P-384
- 更高安全性
- 政府/金融级应用
✅ 首选:secp256k1
- 比特币/以太坊标准
- 性能优异
- 生态成熟
✅ 首选:P-256
- 平衡安全性和性能
- 硬件加速支持广泛
⚠️ 避免:P-521
- 性能开销大
- 嵌入式设备不推荐
✅ 首选:P-384 或 brainpoolP384r1
- 192 位安全强度
- 抗量子计算能力更强 (相对)
087. 安全陷阱与最佳实践
7.1 随机数灾难
// PlayStation 3 漏洞
mbedtls_mpi k;
mbedtls_mpi_lset(&k, 12345); // 固定值!
// 两次签名使用相同 k → 私钥泄露
// 使用 rand() 生成 k
srand(time(NULL));
for (int i = 0; i < 32; i++) {
k_bytes[i] = rand() % 256; // 不安全!
}
// 方案 1:使用确定性签名 (推荐)
#define MBEDTLS_ECDSA_DETERMINISTIC
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256, ...);
// 方案 2:使用密码学安全的 RNG
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, ...);
mbedtls_ecdsa_sign(&grp, &r, &s, &d, hash, 32,
mbedtls_ctr_drbg_random, &ctr_drbg);
7.2 侧信道攻击防护
// ❌ 错误:直接比较签名
if (memcmp(signature1, signature2, sig_len) == 0) {
// memcmp 在发现第一个不同字节时立即返回
// 攻击者可以通过测量时间推断签名内容
}
// ✅ 正确:常量时间比较
if (mbedtls_platform_memequal(signature1, signature2, sig_len) == 0) {
// 始终比较所有字节,时间恒定
}
// 签名时使用盲化因子 t
// s = (e + r·d) / k
// 变为:s = t(e + r·d) / (kt)
// 防止通过功耗分析推断私钥
mbedtls_ecdsa_sign_restartable(
grp, r, s, d, hash, hash_len,
f_rng, p_rng,
f_rng_blind, p_rng_blind, // 盲化 RNG
rs_ctx);
7.3 私钥管理
// 1. 硬编码私钥
constunsignedchar private_key[] = {
0x12, 0x34, 0x56, ... // 永远不要这样做!
};
// 2. 明文存储私钥
FILE *f = fopen("private_key.bin", "wb");
fwrite(private_key, 1, 32, f); // 不安全!
// 3. 不清零敏感数据
mbedtls_mpi d;
// ... 使用 d ...
mbedtls_mpi_free(&d); // 内存中可能残留私钥
// 1. 使用安全存储 (如 TPM, Secure Element)
#ifdef USE_SECURE_ELEMENT
se_generate_keypair(&key_id);
se_ecdsa_sign(key_id, hash, signature);
#endif
// 2. 加密存储私钥
unsignedchar encrypted_key[64];
aes_gcm_encrypt(private_key, 32, encrypted_key, master_key);
// 3. 使用后立即清零
mbedtls_mpi d;
// ... 使用 d ...
mbedtls_platform_zeroize(&d, sizeof(d)); // 安全清零
mbedtls_mpi_free(&d);
7.4 签名可塑性 (Malleability)
问题:给定有效签名 (r, s),攻击者可以构造另一个有效签名 (r, -s mod n)。// 强制 s 值在低半部分:s ≤ n/2
intnormalize_signature_s(mbedtls_mpi *s, const mbedtls_mpi *n)
{
mbedtls_mpi half_n;
mbedtls_mpi_init(&half_n);
// half_n = n / 2
mbedtls_mpi_copy(&half_n, n);
mbedtls_mpi_shift_r(&half_n, 1);
// 如果 s > n/2,则 s = n - s
if (mbedtls_mpi_cmp_mpi(s, &half_n) > 0) {
mbedtls_mpi_sub_mpi(s, n, s);
}
mbedtls_mpi_free(&half_n);
return0;
}
// 签名后规范化
mbedtls_ecdsa_sign(&grp, &r, &s, &d, hash, 32, f_rng, p_rng);
normalize_signature_s(&s, &grp.N); // 确保 s ≤ n/2
7.5 哈希函数选择
// MD5 / SHA-1 已被破解
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_MD5, ...); // 危险!
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA1, ...); // 危险!
// SHA-256 (最常用)
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256, ...);
// SHA-384 (配合 P-384 曲线)
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA384, ...);
// SHA-512 (配合 P-521 曲线)
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA512, ...);
7.6 公钥验证
// 直接使用未验证的公钥
mbedtls_ecp_point Q;
mbedtls_ecp_point_read_binary(&grp, &Q, pubkey_bytes, pubkey_len);
mbedtls_ecdsa_verify(&grp, hash, 32, &Q, &r, &s); // 危险!
mbedtls_ecp_point Q;
mbedtls_ecp_point_read_binary(&grp, &Q, pubkey_bytes, pubkey_len);
// 验证 Q 在曲线上且不是无穷远点
int ret = mbedtls_ecp_check_pubkey(&grp, &Q);
if (ret != 0) {
printf("❌ 无效的公钥!\n");
return ret;
}
// 验证 Q 的阶是 n (防止小子群攻击)
mbedtls_ecp_point nQ;
mbedtls_ecp_mul(&grp, &nQ, &grp.N, &Q, NULL, NULL);
if (!mbedtls_ecp_is_zero(&nQ)) {
printf("❌ 公钥阶不正确!\n");
return-1;
}
// 现在可以安全使用
mbedtls_ecdsa_verify(&grp, hash, 32, &Q, &r, &s);
098. 性能优化技巧
8.1 固定点优化
原理:预计算基点 G 的倍数,加速 k·G 运算。// mbedTLS 配置
#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 // 启用固定点优化
// 性能提升
// P-256 签名:90ms → 60ms (提升 33%)
// P-384 签名:220ms → 150ms (提升 32%)
预计算表:T[i] = i·G, i = 1, 2, ..., 2^w
计算 k·G 时,将 k 分解为 w 位窗口,查表相加
内存开销:P-256 约 1KB,P-384 约 2KB
8.2 窗口大小调优
// mbedTLS 配置
#define MBEDTLS_ECP_WINDOW_SIZE 4 // 默认值
// 窗口大小对性能的影响 (P-256)
// w=2: 226 ms, 内存 256 字节
// w=3: 262 ms, 内存 512 字节
// w=4: 303 ms, 内存 1 KB ← 默认
// w=5: 320 ms, 内存 2 KB
// w=6: 320 ms, 内存 4 KB
8.3 批量验证优化
// 朴素方法:逐个验证
for (int i = 0; i < n; i++) {
mbedtls_ecdsa_verify(&grp, hash[i], 32, &Q[i], &r[i], &s[i]);
}
// 时间:n × 175ms (P-256)
// 批量验证 (使用 Shamir 技巧)
// 验证:∑(u1[i]·G + u2[i]·Q[i]) 的 x 坐标
// 时间:约 (n/2) × 175ms (提升 50%)
intbatch_verify_ecdsa(mbedtls_ecp_group *grp,
constunsignedchar **hashes,
const mbedtls_ecp_point *Q,
const mbedtls_mpi *r,
const mbedtls_mpi *s,
int count)
{
// 1. 生成随机权重 a[i]
mbedtls_mpi a[count];
for (int i = 0; i < count; i++) {
mbedtls_mpi_fill_random(&a[i], 16, f_rng, p_rng);
}
// 2. 计算 ∑ a[i]·(u1[i]·G + u2[i]·Q[i])
mbedtls_ecp_point R;
mbedtls_ecp_point_init(&R);
for (int i = 0; i < count; i++) {
mbedtls_mpi u1, u2, w;
derive_mpi(grp, &e, hashes[i], 32);
mbedtls_mpi_inv_mod(&w, &s[i], &grp->N);
mbedtls_mpi_mul_mpi(&u1, &e, &w);
mbedtls_mpi_mul_mpi(&u2, &r[i], &w);
mbedtls_mpi_mul_mpi(&u1, &u1, &a[i]);
mbedtls_mpi_mul_mpi(&u2, &u2, &a[i]);
mbedtls_ecp_point temp;
mbedtls_ecp_muladd(grp, &temp, &u1, &grp->G, &u2, &Q[i]);
mbedtls_ecp_point_add(grp, &R, &R, &temp);
}
// 3. 验证结果
// (简化:实际需要更复杂的验证逻辑)
return0;
}
8.4 硬件加速
// 自动检测并使用 ARM CE
#if defined(MBEDTLS_HAVE_ARM64)
// mbedTLS 自动使用 ARM64 优化
// P-256 签名:90ms → 15ms (提升 6 倍)
#endif
Intel AES-NI (用于 GCM 模式,间接加速):#if defined(MBEDTLS_AESNI_C)
// 自动启用 AES-NI
#endif
// 使用 TPM 2.0 进行 ECDSA 签名
#ifdef USE_TPM
TPM2_Sign(key_handle, hash, 32, signature, &sig_len);
// 签名时间:~5ms (硬件加速)
// 私钥永不离开 TPM
#endif
8.5 内存优化
// ❌ 大栈分配
voidsign_message(void) {
mbedtls_ecdsa_context ctx; // ~2KB
unsignedchar signature[256];
// ...
}
// ✅ 使用堆分配
voidsign_message(void) {
mbedtls_ecdsa_context *ctx = malloc(sizeof(mbedtls_ecdsa_context));
mbedtls_ecdsa_init(ctx);
// ...
mbedtls_ecdsa_free(ctx);
free(ctx);
}
// 全局 ECDSA 上下文 (多次签名复用)
static mbedtls_ecdsa_context g_ecdsa_ctx;
voidinit_crypto(void) {
mbedtls_ecdsa_init(&g_ecdsa_ctx);
mbedtls_ecdsa_genkey(&g_ecdsa_ctx, MBEDTLS_ECP_DP_SECP256R1, ...);
}
voidsign_multiple_messages(void) {
for (int i = 0; i < 100; i++) {
// 复用同一个上下文,避免重复初始化
mbedtls_ecdsa_write_signature(&g_ecdsa_ctx, ...);
}
}
109. 常见问题 FAQ
Q1: ECDSA 和 RSA 如何选择?
注意:RSA 验证更快,但签名慢;ECDSA 签名和验证都较快。Q2: 为什么比特币不用 P-256 而用 secp256k1?
性能:secp256k1 的 a=0, b=7 简化了运算历史:2009 年比特币诞生时,secp256k1 已存在2013 年斯诺登泄密事件后,NIST 曲线被质疑可能有 NSA 后门Q3: 确定性签名会降低安全性吗?
❌ 如果私钥泄露,攻击者可以生成相同签名 (但这不是新问题)Q4: ECDSA 签名长度为什么不固定?
示例 (P-256):
r=0x00123456... (33 字节,前导 0x00)
s=0x789ABC... (32 字节)
签名 =30450221 [33字节r] 0220 [32字节s] (71 字节)
r=0x123456... (32 字节,无前导 0)
s=0x00789ABC... (33 字节)
签名 =30450220 [32字节r] 0221 [33字节s] (71 字节)
r=0x123456... (32 字节)
s=0x789ABC... (32 字节)
签名 =30440220 [32字节r] 0220 [32字节s] (70 字节)
Q5: 如何防止签名重放攻击?
// 在消息中包含时间戳
char message[256];
snprintf(message, sizeof(message), "%s|%ld", data, time(NULL));
mbedtls_sha256((unsignedchar*)message, strlen(message), hash, 0);
mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256, hash, 32, ...);
// 验证时检查时间戳
if (abs(timestamp - time(NULL)) > 300) { // 5 分钟有效期
return-1; // 签名过期
}
// 服务器生成随机 nonce
uint8_t nonce[16];
mbedtls_ctr_drbg_random(&ctr_drbg, nonce, 16);
// 客户端签名:hash(data || nonce)
// 服务器验证后,标记 nonce 为已使用
// 每个签名包含递增的序列号
typedefstruct {
uint64_t seq_num;
uint8_t data[256];
uint8_t signature[72];
} signed_message_t;
// 验证时检查序列号
if (msg->seq_num <= last_seq_num) {
return-1; // 重放攻击
}
last_seq_num = msg->seq_num;
Q6: ECDSA 能抵抗量子计算攻击吗?
不能! Shor 算法可以在多项式时间内破解 ECDLP。// 混合签名 (ECDSA + 后量子)
sign_hybrid(message, ecdsa_key, pq_key, signature);
// 只有同时破解两种算法才能伪造签名
1110. 总结与检查清单
10.1 核心要点回顾
签名:证明"我拥有私钥 d,且我认可这条消息"
验证:确认"签名者确实拥有对应公钥的私钥"
数学基础:椭圆曲线离散对数难题 (ECDLP)
安全性:256 位 ECC ≈ 3072 位 RSA
密钥生成:d ← random(1, n-1), Q = d·G
签名: (r, s) = (k·G).x, k⁻¹(e + r·d) mod n
验证: 检查 (u₁·G + u₂·Q).x == r
10.2 安全检查清单
[ ] 使用后立即清零敏感数据 (mbedtls_platform_zeroize)[ ] 启用确定性签名 (MBEDTLS_ECDSA_DETERMINISTIC)[ ] 验证公钥在曲线上 (mbedtls_ecp_check_pubkey)[ ] TLS/Web:P-256 (secp256r1)[ ] 高安全:P-384 或 brainpoolP384r1[ ] 启用固定点优化 (MBEDTLS_ECP_FIXED_POINT_OPTIM)[ ] 调整窗口大小 (MBEDTLS_ECP_WINDOW_SIZE)[ ] 使用硬件加速 (ARM CE, AES-NI)10.3 常见错误速查
| | |
|---|
| | |
| | |
| | 调用 mbedtls_ecp_check_pubkey |
| | |
| | |
| | 使用 mbedtls_platform_zeroize |
| | |
10.4 性能基准 (参考)
10.5 相关源文件映射
| | |
|---|
| ECDSA 核心 | ecdsa.c | ecdsa.h |
| 椭圆曲线运算 | ecp.c | ecp.h |
| 曲线参数 | ecp_curves.c | |
| 大数运算 | bignum.c | bignum.h |
| 确定性签名 | hmac_drbg.c | hmac_drbg.h |
| ASN.1 编码 | asn1write.c | asn1write.h |
10.6 进一步学习
SEC1: Elliptic Curve CryptographyRFC 6979: Deterministic ECDSAFIPS 186-4: Digital Signature Standard《椭圆曲线密码学导论》- Darrel Hankerson《密码工程》- Niels Ferguson (第 6 章)
12📚 完整示例代码
#include"mbedtls/ecdsa.h"
#include"mbedtls/sha256.h"
#include"mbedtls/entropy.h"
#include"mbedtls/ctr_drbg.h"
#include<stdio.h>
#include<string.h>
intmain(void)
{
int ret;
mbedtls_ecdsa_context ctx;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
unsignedchar hash[32];
unsignedchar signature[MBEDTLS_ECDSA_MAX_LEN];
size_t sig_len;
constchar *message = "Hello, ECDSA!";
// 1. 初始化
mbedtls_ecdsa_init(&ctx);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
printf("🔐 ECDSA 签名验证示例\n\n");
// 2. 初始化随机数生成器
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(constunsignedchar *)"ecdsa_demo", 10);
if (ret != 0) {
printf("❌ RNG 初始化失败: -0x%04x\n", -ret);
goto cleanup;
}
// 3. 生成 ECDSA 密钥对 (P-256)
printf("📝 生成 P-256 密钥对...\n");
ret = mbedtls_ecdsa_genkey(&ctx, MBEDTLS_ECP_DP_SECP256R1,
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) {
printf("❌ 密钥生成失败: -0x%04x\n", -ret);
goto cleanup;
}
printf("✅ 密钥生成成功!\n\n");
// 4. 计算消息哈希
printf("📝 消息: \"%s\"\n", message);
mbedtls_sha256((constunsignedchar *)message, strlen(message), hash, 0);
printf("📝 SHA-256: ");
for (int i = 0; i < 32; i++) {
printf("%02x", hash[i]);
}
printf("\n\n");
// 5. 生成签名
printf("✍️ 生成 ECDSA 签名...\n");
ret = mbedtls_ecdsa_write_signature(&ctx, MBEDTLS_MD_SHA256,
hash, 32,
signature, sizeof(signature), &sig_len,
mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret != 0) {
printf("❌ 签名失败: -0x%04x\n", -ret);
goto cleanup;
}
printf("✅ 签名成功!长度: %zu 字节\n", sig_len);
printf("📝 签名: ");
for (size_t i = 0; i < sig_len; i++) {
printf("%02x", signature[i]);
}
printf("\n\n");
// 6. 验证签名
printf("🔍 验证签名...\n");
ret = mbedtls_ecdsa_read_signature(&ctx, hash, 32, signature, sig_len);
if (ret == 0) {
printf("✅ 签名验证成功!\n");
} else {
printf("❌ 签名验证失败: -0x%04x\n", -ret);
}
// 7. 测试篡改检测
printf("\n🔍 测试篡改检测...\n");
hash[0] ^= 0x01; // 修改哈希的一个位
ret = mbedtls_ecdsa_read_signature(&ctx, hash, 32, signature, sig_len);
if (ret != 0) {
printf("✅ 成功检测到篡改!\n");
} else {
printf("❌ 未能检测到篡改!\n");
}
cleanup:
mbedtls_ecdsa_free(&ctx);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}
gcc -o ecdsa_demo ecdsa_demo.c -lmbedtls -lmbedx509 -lmbedcrypto
./ecdsa_demo
🔐 ECDSA 签名验证示例
📝 生成 P-256 密钥对...
✅ 密钥生成成功!
📝 消息: "Hello, ECDSA!"
📝 SHA-256: 3f4c8b9e2d1a7f6e5c4b3a2d1e0f9c8b7a6e5d4c3b2a1f0e9d8c7b6a5e4d3c2b
✍️ 生成 ECDSA 签名...
✅ 签名成功!长度: 70 字节
📝 签名: 3044022012345678...
🔍 验证签名...
✅ 签名验证成功!
🔍 测试篡改检测...
✅ 成功检测到篡改!
🎓 恭喜!你已经掌握了 ECDSA 椭圆曲线数字签名!