引言:数字世界中的"亲笔签名"

想象一下,您正在数字世界中签署一份重要文件。如何确保这个签名是您亲手所签,且不被他人伪造?这就是数字签名算法诞生的初衷。数字签名如同现实世界的印章和签名,但在数学的严密性下,达到了前所未有的安全水平。

数字签名的演变之路

1980年代,随着公钥密码学的兴起,数字签名开始崭露头角。从最初的RSA签名到如今的多种算法并存,数字签名技术经历了从简单到复杂、从易受到安全的演进过程。在这个过程中,ECDSA和RSASSA-PSS成为了两个重要的里程碑。

第一部分:ECDSA - 椭圆曲线的优雅之舞

1.1 椭圆曲线:从数学到密码学的奇妙旅程

椭圆曲线密码学源于一个看似简单的问题:如何找到满足方程 y² = x³ + ax + b 的点?这些点形成的图形虽然名为"椭圆曲线",但看起来一点也不像椭圆!

关键特性:

  • 加法运算的封闭性:曲线上任意两点相加,结果仍在曲线上
  • 离散对数问题的困难性:已知点P和kP,求k极其困难(这个"困难"是安全的基石)
  • 密钥尺寸小:同等安全强度下,椭圆曲线密钥远小于RSA密钥
# 椭圆曲线方程示例(secp256k1曲线,比特币使用)
y² = x³ + 7 (mod p)
其中 p = 2²⁵⁶ - 2³² - 2⁹ - 2⁸ - 2⁷ - 2⁶ - 2⁴ - 1

1.2 ECDSA算法详解:四步曲的精密协奏

让我们将ECDSA算法拆解为四个清晰步骤:

步骤1:密钥生成 - 铸造您的数字印章
椭圆曲线参数 = (曲线类型, 基点G, 阶n)
私钥d = 随机选择 [1, n-1] 之间的整数
公钥Q = d × G (椭圆曲线标量乘法)

可视化示例:

私钥生成:在巨大的数字海洋中随机抛锚
公钥计算:通过椭圆曲线乘法将私钥"映射"到曲线上
步骤2:签名生成 - 为消息烙上独特印记

当您要签署消息m时:

// 以下是签名生成的核心步骤(概念性代码)
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/sha.h>

/**
 * @brief 生成ECDSA签名
 * @param[in] private_key 私钥
 * @param[in] message 待签名的消息
 * @param[in] message_len 消息长度
 * @param[out] signature 生成的签名(输出参数)
 * @param[out] sig_len 签名长度(输出参数)
 * @return 成功返回1,失败返回0
 */
int ecdsa_sign(const EC_KEY *private_key, 
               const unsigned char *message,
               size_t message_len,
               unsigned char *signature,
               unsigned int *sig_len) {
    // 1. 计算消息的哈希值
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, hash);
    
    // 2. 使用ECDSA签名
    return ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH, 
                     signature, sig_len, private_key);
}

签名生成流程详解:

  1. 计算哈希:将任意长度的消息压缩为固定长度的"数字指纹"
  2. 生成随机数k:这是签名的"盐",确保每次签名都不同
  3. 计算临时点:R = k × G,取其x坐标r = R.x mod n
  4. 计算签名值:s = k⁻¹(hash + d·r) mod n
  5. 输出签名:(r, s) 对

流程图解:

消息 → SHA-256哈希 → 随机数k生成 → 计算R = k×G → 提取r → 计算s → 签名(r,s)
步骤3:签名验证 - 真伪鉴定的艺术

验证者需要确认签名是否有效:

/**
 * @brief 验证ECDSA签名
 * @param[in] public_key 公钥
 * @param[in] message 原始消息
 * @param[in] message_len 消息长度
 * @param[in] signature 待验证的签名
 * @param[in] sig_len 签名长度
 * @return 签名有效返回1,无效返回0,错误返回-1
 */
int ecdsa_verify(const EC_KEY *public_key,
                 const unsigned char *message,
                 size_t message_len,
                 const unsigned char *signature,
                 unsigned int sig_len) {
    // 1. 计算消息哈希
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, hash);
    
    // 2. 验证ECDSA签名
    return ECDSA_verify(0, hash, SHA256_DIGEST_LENGTH,
                       signature, sig_len, public_key);
}

验证流程详解:

  1. 计算哈希:与签名时相同的方式处理消息
  2. 计算中间值:w = s⁻¹ mod n
  3. 计算点:u₁ = hash·w mod n, u₂ = r·w mod n
  4. 计算验证点:V = u₁×G + u₂×Q
  5. 验证:检查V.x mod n 是否等于 r

数学原理证明:

验证方程推导:
已知:s = k⁻¹(hash + d·r) mod n
推导:k = s⁻¹(hash + d·r) mod n
     = hash·s⁻¹ + d·r·s⁻¹ mod n
     = u₁ + d·u₂ mod n

因此:k×G = u₁×G + u₂×(d×G)
        = u₁×G + u₂×Q
        = V

由于R = k×G,且r = R.x mod n
所以V.x mod n 应该等于 r
步骤4:安全性与实现细节

关键安全考虑:

  1. 随机数k的重要性:k必须真正随机且唯一,否则私钥可能泄露
  2. 侧信道攻击防护:实现时需防范时序攻击、功耗分析等
  3. 曲线选择:不同曲线有不同的安全性和性能特性

1.3 ECDSA在实际中的应用:比特币的交易验证

让我们以比特币交易为例,看看ECDSA如何保障加密货币的安全:

// 比特币交易签名的简化示例
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <stdint.h>

/**
 * @brief 比特币交易签名结构
 */
typedef struct {
    uint8_t r[32];      // 签名r值
    uint8_t s[32];      // 签名s值
    uint8_t recovery_id; // 恢复ID(用于恢复公钥)
} bitcoin_signature_t;

/**
 * @brief 签署比特币交易
 * @param[in] private_key 私钥(32字节)
 * @param[in] transaction 交易数据
 * @param[in] tx_len 交易长度
 * @param[out] sig 输出签名
 * @return 成功返回1,失败返回0
 */
int sign_bitcoin_transaction(const uint8_t private_key[32],
                            const uint8_t *transaction,
                            size_t tx_len,
                            bitcoin_signature_t *sig) {
    // 1. 序列化交易数据
    uint8_t tx_hash[32];
    double_sha256(transaction, tx_len, tx_hash); // 比特币使用双SHA-256
    
    // 2. 创建ECDSA签名(使用secp256k1曲线)
    ECDSA_SIG *ecdsa_sig = ECDSA_do_sign(tx_hash, 32, 
                                        load_private_key(private_key));
    
    if (!ecdsa_sig) return 0;
    
    // 3. 提取r和s值
    const BIGNUM *r, *s;
    ECDSA_SIG_get0(ecdsa_sig, &r, &s);
    
    // 4. 转换为规范格式
    BN_bn2binpad(r, sig->r, 32);
    BN_bn2binpad(s, sig->s, 32);
    
    // 5. 计算恢复ID(用于公钥恢复)
    sig->recovery_id = calculate_recovery_id(r, s, tx_hash);
    
    ECDSA_SIG_free(ecdsa_sig);
    return 1;
}

比特币中的ECDSA流程:

交易数据 → 双SHA-256哈希 → ECDSA签名 → 包含在交易中 → 全网验证

第二部分:RSASSA-PSS - 概率签名的智慧设计

2.1 RSA的数学基础:大数分解的难题

RSA的安全性基于一个简单而深刻的事实:将两个大质数相乘很容易,但将乘积分解回质数极其困难。

数学原理:

密钥生成:
1. 选择两个大质数 p 和 q
2. 计算 n = p × q
3. 计算 φ(n) = (p-1)(q-1)
4. 选择 e 使得 1 < e < φ(n) 且 gcd(e, φ(n)) = 1
5. 计算 d 使得 d × e ≡ 1 (mod φ(n))

公钥: (n, e)
私钥: (n, d)

加密: c = m^e mod n
解密: m = c^d mod n
签名: s = m^d mod n
验证: m = s^e mod n

2.2 PSS填充方案:为什么需要"加盐"?

传统的RSA签名存在安全隐患,特别是确定性签名容易受到选择明文攻击。PSS(Probabilistic Signature Scheme)通过引入随机性解决了这一问题。

PSS的核心思想:

  1. 随机化:每次签名都不同,即使对同一消息
  2. 编码结构:确保签名格式的一致性
  3. 安全性证明:可证明安全(在随机预言机模型下)

2.3 RSASSA-PSS算法详解:精心设计的签名流水线

步骤1:编码操作 - 消息的"包装艺术"
EM = MGF1(盐) ⊕ 填充字符串 || 哈希(消息) || 盐 || BC

其中:

  • MGF1:掩码生成函数,通常基于SHA-256
  • 盐:随机生成,通常与哈希输出长度相同
  • BC:固定的尾部字节
// PSS编码的核心过程(概念性代码)
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rand.h>

/**
 * @brief PSS编码函数
 * @param[in] mHash 消息哈希值
 * @param[in] salt_len 盐的长度
 * @param[out] EM 编码后的消息(输出参数)
 * @param[in] emBits 编码消息的比特长度
 * @return 成功返回1,失败返回0
 */
int pss_encode(const unsigned char *mHash,
               int salt_len,
               unsigned char *EM,
               int emBits) {
    int hLen = SHA256_DIGEST_LENGTH; // 哈希长度
    int emLen = (emBits + 7) / 8;    // 字节长度
    
    // 1. 生成随机盐
    unsigned char *salt = malloc(salt_len);
    RAND_bytes(salt, salt_len);
    
    // 2. 构造数据块 M'
    // M' = 填充字符串 || mHash || 盐
    unsigned char M_prime[8 + hLen + salt_len];
    memset(M_prime, 0, 8); // 前8字节为0
    
    memcpy(M_prime + 8, mHash, hLen);
    memcpy(M_prime + 8 + hLen, salt, salt_len);
    
    // 3. 计算 H = Hash(M')
    unsigned char H[SHA256_DIGEST_LENGTH];
    SHA256(M_prime, sizeof(M_prime), H);
    
    // 4. 构造数据块 DB
    // DB = 填充字符串2 || 盐
    unsigned char *DB = malloc(emLen - hLen - 1);
    memset(DB, 0, emLen - hLen - salt_len - 2);
    DB[emLen - hLen - salt_len - 2] = 0x01;
    memcpy(DB + emLen - hLen - salt_len - 1, salt, salt_len);
    
    // 5. 生成掩码 dbMask
    unsigned char *dbMask = malloc(emLen - hLen - 1);
    mgf1(dbMask, emLen - hLen - 1, H, hLen);
    
    // 6. 计算 maskedDB = DB ⊕ dbMask
    for (int i = 0; i < emLen - hLen - 1; i++) {
        EM[i] = DB[i] ^ dbMask[i];
    }
    
    // 7. 设置最左边的比特为0(确保EM < n)
    EM[0] &= 0x7F;
    
    // 8. 组合最终EM
    memcpy(EM + emLen - hLen - 1, H, hLen);
    EM[emLen - 1] = 0xBC;
    
    free(salt);
    free(DB);
    free(dbMask);
    return 1;
}
步骤2:签名生成 - RSA加密的精髓
/**
 * @brief 生成RSASSA-PSS签名
 * @param[in] rsa_key RSA私钥
 * @param[in] message 待签名的消息
 * @param[in] message_len 消息长度
 * @param[out] signature 生成的签名(输出参数)
 * @param[out] sig_len 签名长度(输出参数)
 * @return 成功返回1,失败返回0
 */
int rsassa_pss_sign(RSA *rsa_key,
                    const unsigned char *message,
                    size_t message_len,
                    unsigned char *signature,
                    unsigned int *sig_len) {
    // 1. 计算消息哈希
    unsigned char mHash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, mHash);
    
    // 2. 获取RSA模数长度
    int rsa_size = RSA_size(rsa_key);
    int emBits = rsa_size * 8 - 1; // 留出1比特确保签名 < n
    
    // 3. 使用PSS编码
    unsigned char *EM = malloc(rsa_size);
    pss_encode(mHash, SHA256_DIGEST_LENGTH, EM, emBits);
    
    // 4. RSA私钥操作:s = EM^d mod n
    int result = RSA_private_encrypt(rsa_size, EM, signature,
                                     rsa_key, RSA_NO_PADDING);
    
    *sig_len = result;
    free(EM);
    return (result > 0) ? 1 : 0;
}
步骤3:签名验证 - 解构与确认
/**
 * @brief 验证RSASSA-PSS签名
 * @param[in] rsa_key RSA公钥
 * @param[in] message 原始消息
 * @param[in] message_len 消息长度
 * @param[in] signature 待验证的签名
 * @param[in] sig_len 签名长度
 * @return 签名有效返回1,无效返回0,错误返回-1
 */
int rsassa_pss_verify(RSA *rsa_key,
                      const unsigned char *message,
                      size_t message_len,
                      const unsigned char *signature,
                      unsigned int sig_len) {
    // 1. 计算消息哈希
    unsigned char mHash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, mHash);
    
    // 2. RSA公钥操作:EM' = s^e mod n
    int rsa_size = RSA_size(rsa_key);
    unsigned char *EM_prime = malloc(rsa_size);
    
    int result = RSA_public_decrypt(rsa_size, signature, EM_prime,
                                    rsa_key, RSA_NO_PADDING);
    
    if (result != rsa_size) {
        free(EM_prime);
        return -1;
    }
    
    // 3. 验证PSS编码
    int emBits = rsa_size * 8 - 1;
    int verification = pss_verify(mHash, SHA256_DIGEST_LENGTH,
                                  EM_prime, emBits);
    
    free(EM_prime);
    return verification;
}

2.4 PSS验证的详细流程

PSS验证是一个反向解码的过程:

1. 解码结构检查:
   - 确认尾部字节为0xBC
   - 提取H'(哈希值部分)
   - 提取maskedDB(掩码后的数据部分)

2. 恢复DB:
   dbMask = MGF1(H', emLen - hLen - 1)
   DB = maskedDB ⊕ dbMask

3. 结构验证:
   - 确认最左边比特为0
   - 提取盐值
   - 验证填充字符串

4. 哈希验证:
   重新计算 H = Hash(0...0 || mHash || 盐)
   比较 H 与 H'

2.5 RSASSA-PSS在现代TLS/SSL中的应用

在TLS 1.3中,RSASSA-PSS成为推荐的RSA签名方案:

// TLS 1.3中证书签名验证的简化示例
#include <openssl/x509.h>
#include <openssl/pem.h>

/**
 * @brief 验证TLS证书的RSASSA-PSS签名
 * @param[in] cert_file 证书文件路径
 * @param[in] ca_file CA证书文件路径
 * @return 验证成功返回1,失败返回0
 */
int verify_tls_certificate(const char *cert_file, const char *ca_file) {
    // 1. 加载CA证书
    FILE *ca_fp = fopen(ca_file, "r");
    X509 *ca_cert = PEM_read_X509(ca_fp, NULL, NULL, NULL);
    fclose(ca_fp);
    
    // 2. 加载待验证证书
    FILE *cert_fp = fopen(cert_file, "r");
    X509 *cert = PEM_read_X509(cert_fp, NULL, NULL, NULL);
    fclose(cert_fp);
    
    // 3. 提取CA公钥
    EVP_PKEY *ca_pubkey = X509_get_pubkey(ca_cert);
    
    // 4. 创建验证上下文
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    
    // 5. 初始化PSS验证参数
    EVP_PKEY_CTX *pkey_ctx = NULL;
    EVP_DigestVerifyInit(ctx, &pkey_ctx, 
                        EVP_sha256(), NULL, ca_pubkey);
    
    // 设置PSS参数
    EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
    EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO);
    
    // 6. 获取证书的签名信息
    ASN1_BIT_STRING *signature = cert->signature;
    X509_ALGOR *sig_alg = cert->sig_alg;
    
    // 7. 计算证书的TBSCertificate哈希
    unsigned char tbs_hash[SHA256_DIGEST_LENGTH];
    unsigned char *tbs_data = NULL;
    int tbs_len = i2d_re_X509_tbs(cert, &tbs_data);
    SHA256(tbs_data, tbs_len, tbs_hash);
    
    // 8. 验证签名
    int result = EVP_DigestVerify(ctx, signature->data, signature->length,
                                  tbs_hash, SHA256_DIGEST_LENGTH);
    
    // 9. 清理资源
    EVP_MD_CTX_free(ctx);
    EVP_PKEY_free(ca_pubkey);
    X509_free(ca_cert);
    X509_free(cert);
    free(tbs_data);
    
    return (result == 1) ? 1 : 0;
}

第三部分:深度对比与选择指南

3.1 性能对比:速度与资源的平衡

特性ECDSA (P-256)RSASSA-PSS (2048位)RSASSA-PSS (3072位)
签名速度快 (0.3ms)中等 (1.2ms)慢 (3.5ms)
验证速度中等 (0.6ms)快 (0.1ms)中等 (0.3ms)
密钥大小32字节私钥
64字节公钥
256字节私钥
256字节公钥
384字节私钥
384字节公钥
签名大小64字节256字节384字节
安全强度128位112位128位
内存占用低中等高

3.2 安全性分析:攻击面与防御机制

ECDSA的攻击面:
  1. 随机数重用:如果k值重复,私钥可能泄露
  2. 侧信道攻击:通过时间、功耗等物理泄露获取信息
  3. 曲线选择:弱曲线可能导致安全问题
RSASSA-PSS的攻击面:
  1. 填充预言机攻击:已通过PSS的概率特性缓解
  2. 大数分解进展:量子计算机的潜在威胁
  3. 实现漏洞:如Bleichenbacher攻击

3.3 选择指南:何时使用哪种算法?

选择ECDSA当:
  • ✅ 资源受限环境(物联网设备、智能卡)
  • ✅ 带宽敏感应用(区块链交易、移动通信)
  • ✅ 需要短签名(证书链优化)
  • ✅ 批量签名操作(服务器端)
选择RSASSA-PSS当:
  • ✅ 需要兼容传统系统
  • ✅ 验证性能是关键需求(TLS握手)
  • ✅ 硬件加速支持良好(HSM、智能卡)
  • ✅ 需要可证明的安全性保证

3.4 量子计算时代的考量

量子计算机对两种算法的影响:

  1. ECDSA:易受Shor算法攻击,256位曲线在量子计算机下安全性降至128位
  2. RSA:同样易受Shor算法攻击,3072位RSA在量子下安全性降至约100位

后量子密码学选项:

  • 基于格的签名(如Dilithium)
  • 基于哈希的签名(如SPHINCS+)
  • 基于多变量的签名

第四部分:完整可运行代码示例

4.1 Makefile:构建自动化

# Makefile for ECDSA and RSASSA-PSS examples
CC = gcc
CFLAGS = -Wall -O2 -std=c11
LDFLAGS = -lssl -lcrypto -lm

# Detect OpenSSL version
OPENSSL_CFLAGS := $(shell pkg-config --cflags openssl 2>/dev/null || echo "")
OPENSSL_LIBS := $(shell pkg-config --libs openssl 2>/dev/null || echo "-lssl -lcrypto")

# Default target
all: ecdsa_example rsassa_pss_example comparison

# ECDSA example
ecdsa_example: ecdsa_example.c
	$(CC) $(CFLAGS) $(OPENSSL_CFLAGS) -o $@ $< $(OPENSSL_LIBS)

# RSASSA-PSS example
rsassa_pss_example: rsassa_pss_example.c
	$(CC) $(CFLAGS) $(OPENSSL_CFLAGS) -o $@ $< $(OPENSSL_LIBS)

# Comparison tool
comparison: comparison.c
	$(CC) $(CFLAGS) $(OPENSSL_CFLAGS) -o $@ $< $(OPENSSL_LIBS)

# Clean up
clean:
	rm -f ecdsa_example rsassa_pss_example comparison *.pem *.sig *.key

# Test targets
test-ecdsa: ecdsa_example
	./ecdsa_example

test-rsa: rsassa_pss_example
	./rsassa_pss_example

test-all: test-ecdsa test-rsa
	./comparison

.PHONY: all clean test-ecdsa test-rsa test-all

4.2 ECDSA完整示例代码

/**
 * @file ecdsa_example.c
 * @brief 完整的ECDSA签名和验证示例
 * 
 * 这个程序演示了如何使用OpenSSL库生成ECDSA密钥对,
 * 对消息进行签名,然后验证签名。
 * 
 * 编译命令: make ecdsa_example
 * 运行命令: ./ecdsa_example
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/bn.h>

/**
 * @brief 生成ECDSA密钥对
 * @param[in] curve_name 椭圆曲线名称(如"NID_secp256k1")
 * @param[out] private_key 生成的私钥(输出参数)
 * @param[out] public_key 生成的公钥(输出参数)
 * @return 成功返回1,失败返回0
 */
int generate_ecdsa_keypair(int curve_name, 
                          EC_KEY **private_key,
                          EC_KEY **public_key) {
    // 创建密钥结构
    EC_KEY *key = EC_KEY_new_by_curve_name(curve_name);
    if (!key) {
        fprintf(stderr, "Error creating EC key structure\n");
        return 0;
    }
    
    // 生成密钥对
    if (!EC_KEY_generate_key(key)) {
        fprintf(stderr, "Error generating EC key pair\n");
        EC_KEY_free(key);
        return 0;
    }
    
    // 确保私钥设置了适当的参数
    if (!EC_KEY_check_key(key)) {
        fprintf(stderr, "Error: invalid EC key generated\n");
        EC_KEY_free(key);
        return 0;
    }
    
    *private_key = key;
    
    // 复制公钥
    *public_key = EC_KEY_new_by_curve_name(curve_name);
    if (!*public_key) {
        EC_KEY_free(key);
        return 0;
    }
    
    const EC_POINT *pub_point = EC_KEY_get0_public_key(key);
    if (!EC_KEY_set_public_key(*public_key, pub_point)) {
        EC_KEY_free(key);
        EC_KEY_free(*public_key);
        return 0;
    }
    
    return 1;
}

/**
 * @brief 使用ECDSA签名消息
 * @param[in] private_key 私钥
 * @param[in] message 待签名的消息
 * @param[in] message_len 消息长度
 * @param[out] signature 生成的签名(输出参数)
 * @param[out] sig_len 签名长度(输出参数)
 * @return 成功返回1,失败返回0
 */
int ecdsa_sign_message(EC_KEY *private_key,
                      const unsigned char *message,
                      size_t message_len,
                      unsigned char **signature,
                      size_t *sig_len) {
    // 计算消息的SHA-256哈希
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, hash);
    
    // 计算签名长度
    *sig_len = ECDSA_size(private_key);
    *signature = malloc(*sig_len);
    if (!*signature) {
        fprintf(stderr, "Memory allocation failed\n");
        return 0;
    }
    
    // 生成ECDSA签名
    unsigned int tmp_sig_len;
    if (!ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH,
                   *signature, &tmp_sig_len, private_key)) {
        fprintf(stderr, "Error creating ECDSA signature\n");
        free(*signature);
        return 0;
    }
    
    *sig_len = tmp_sig_len;
    return 1;
}

/**
 * @brief 验证ECDSA签名
 * @param[in] public_key 公钥
 * @param[in] message 原始消息
 * @param[in] message_len 消息长度
 * @param[in] signature 待验证的签名
 * @param[in] sig_len 签名长度
 * @return 签名有效返回1,无效返回0,错误返回-1
 */
int ecdsa_verify_signature(EC_KEY *public_key,
                          const unsigned char *message,
                          size_t message_len,
                          const unsigned char *signature,
                          size_t sig_len) {
    // 计算消息的SHA-256哈希
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(message, message_len, hash);
    
    // 验证ECDSA签名
    int result = ECDSA_verify(0, hash, SHA256_DIGEST_LENGTH,
                             signature, sig_len, public_key);
    
    return result;
}

/**
 * @brief 将签名转换为DER格式
 * @param[in] r BIGNUM格式的r值
 * @param[in] s BIGNUM格式的s值
 * @param[out] der 输出的DER编码
 * @param[out] der_len DER编码长度
 * @return 成功返回1,失败返回0
 */
int ecdsa_signature_to_der(const BIGNUM *r, const BIGNUM *s,
                          unsigned char **der, size_t *der_len) {
    ECDSA_SIG *sig = ECDSA_SIG_new();
    if (!sig) return 0;
    
    // 注意:ECDSA_SIG_set0会转移所有权,所以需要复制
    BIGNUM *r_copy = BN_dup(r);
    BIGNUM *s_copy = BN_dup(s);
    
    if (!r_copy || !s_copy || !ECDSA_SIG_set0(sig, r_copy, s_copy)) {
        BN_free(r_copy);
        BN_free(s_copy);
        ECDSA_SIG_free(sig);
        return 0;
    }
    
    // 计算DER编码长度
    *der_len = i2d_ECDSA_SIG(sig, NULL);
    *der = malloc(*der_len);
    
    if (!*der) {
        ECDSA_SIG_free(sig);
        return 0;
    }
    
    // 编码为DER
    unsigned char *p = *der;
    *der_len = i2d_ECDSA_SIG(sig, &p);
    
    ECDSA_SIG_free(sig);
    return 1;
}

/**
 * @brief 打印十六进制数据
 * @param[in] label 标签
 * @param[in] data 数据
 * @param[in] len 数据长度
 */
void print_hex(const char *label, const unsigned char *data, size_t len) {
    printf("%s (%zu bytes):\n", label, len);
    for (size_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
        if ((i + 1) % 32 == 0) printf("\n");
        else if ((i + 1) % 4 == 0) printf(" ");
    }
    printf("\n\n");
}

/**
 * @brief 主函数:演示ECDSA签名和验证
 */
int main() {
    printf("=== ECDSA签名和验证示例 ===\n\n");
    
    // 初始化OpenSSL
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    
    EC_KEY *private_key = NULL;
    EC_KEY *public_key = NULL;
    unsigned char *signature = NULL;
    size_t sig_len = 0;
    
    // 1. 生成密钥对
    printf("1. 生成ECDSA密钥对 (使用secp256k1曲线)...\n");
    if (!generate_ecdsa_keypair(NID_secp256k1, &private_key, &public_key)) {
        fprintf(stderr, "密钥对生成失败\n");
        goto cleanup;
    }
    printf("   密钥对生成成功!\n\n");
    
    // 2. 准备消息
    const char *message = "这是一条需要签名的测试消息";
    printf("2. 准备消息:\n");
    printf("   \"%s\"\n\n", message);
    
    // 3. 生成签名
    printf("3. 生成ECDSA签名...\n");
    if (!ecdsa_sign_message(private_key, 
                           (const unsigned char *)message, 
                           strlen(message),
                           &signature, &sig_len)) {
        fprintf(stderr, "签名生成失败\n");
        goto cleanup;
    }
    printf("   签名生成成功!\n");
    print_hex("   签名", signature, sig_len);
    
    // 4. 验证签名
    printf("4. 验证签名...\n");
    int verify_result = ecdsa_verify_signature(public_key,
                                              (const unsigned char *)message,
                                              strlen(message),
                                              signature, sig_len);
    
    if (verify_result == 1) {
        printf("   ✓ 签名验证成功!\n\n");
    } else if (verify_result == 0) {
        printf("   ✗ 签名验证失败!\n\n");
    } else {
        printf("   ! 验证过程中发生错误\n\n");
    }
    
    // 5. 演示篡改检测
    printf("5. 演示篡改检测...\n");
    unsigned char *tampered_signature = malloc(sig_len);
    memcpy(tampered_signature, signature, sig_len);
    
    // 篡改签名的一个字节
    tampered_signature[10] ^= 0xFF;
    
    int tamper_result = ecdsa_verify_signature(public_key,
                                              (const unsigned char *)message,
                                              strlen(message),
                                              tampered_signature, sig_len);
    
    if (tamper_result == 1) {
        printf("   ✗ 篡改后的签名竟然验证通过了!\n");
    } else {
        printf("   ✓ 成功检测到签名被篡改!\n");
    }
    
    free(tampered_signature);
    
cleanup:
    // 清理资源
    if (private_key) EC_KEY_free(private_key);
    if (public_key) EC_KEY_free(public_key);
    if (signature) free(signature);
    
    // 清理OpenSSL
    EVP_cleanup();
    ERR_free_strings();
    
    return 0;
}

4.3 RSASSA-PSS完整示例代码

/**
 * @file rsassa_pss_example.c
 * @brief 完整的RSASSA-PSS签名和验证示例
 * 
 * 这个程序演示了如何使用OpenSSL库生成RSA密钥对,
 * 使用PSS填充方案对消息进行签名和验证。
 * 
 * 编译命令: make rsassa_pss_example
 * 运行命令: ./rsassa_pss_example
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/rand.h>

#define RSA_KEY_BITS 2048

/**
 * @brief 生成RSA密钥对
 * @param[in] bits 密钥位数
 * @param[out] private_key 生成的私钥(输出参数)
 * @param[out] public_key 生成的公钥(输出参数)
 * @return 成功返回1,失败返回0
 */
int generate_rsa_keypair(int bits, EVP_PKEY **private_key, EVP_PKEY **public_key) {
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx) {
        fprintf(stderr, "Error creating EVP_PKEY_CTX\n");
        return 0;
    }
    
    // 初始化密钥生成上下文
    if (EVP_PKEY_keygen_init(ctx) <= 0) {
        fprintf(stderr, "Error initializing keygen\n");
        EVP_PKEY_CTX_free(ctx);
        return 0;
    }
    
    // 设置RSA密钥位数
    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) {
        fprintf(stderr, "Error setting RSA key bits\n");
        EVP_PKEY_CTX_free(ctx);
        return 0;
    }
    
    // 生成密钥对
    if (EVP_PKEY_keygen(ctx, private_key) <= 0) {
        fprintf(stderr, "Error generating RSA key pair\n");
        EVP_PKEY_CTX_free(ctx);
        return 0;
    }
    
    EVP_PKEY_CTX_free(ctx);
    
    // 从私钥提取公钥
    BIO *bio = BIO_new(BIO_s_mem());
    if (!bio) {
        return 0;
    }
    
    if (PEM_write_bio_PUBKEY(bio, *private_key) <= 0) {
        BIO_free(bio);
        return 0;
    }
    
    *public_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
    BIO_free(bio);
    
    return (*public_key != NULL);
}

/**
 * @brief 使用RSASSA-PSS签名消息
 * @param[in] private_key 私钥
 * @param[in] message 待签名的消息
 * @param[in] message_len 消息长度
 * @param[out] signature 生成的签名(输出参数)
 * @param[out] sig_len 签名长度(输出参数)
 * @return 成功返回1,失败返回0
 */
int rsassa_pss_sign(EVP_PKEY *private_key,
                   const unsigned char *message,
                   size_t message_len,
                   unsigned char **signature,
                   size_t *sig_len) {
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (!ctx) {
        fprintf(stderr, "Error creating EVP_MD_CTX\n");
        return 0;
    }
    
    // 初始化签名上下文
    EVP_PKEY_CTX *pkey_ctx = NULL;
    if (EVP_DigestSignInit(ctx, &pkey_ctx, EVP_sha256(), NULL, private_key) <= 0) {
        fprintf(stderr, "Error initializing digest sign\n");
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    // 设置PSS填充参数
    if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
        fprintf(stderr, "Error setting RSA PSS padding\n");
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    // 设置盐长度(自动确定)
    if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO) <= 0) {
        fprintf(stderr, "Error setting salt length\n");
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    // 计算签名长度
    if (EVP_DigestSign(ctx, NULL, sig_len, message, message_len) <= 0) {
        fprintf(stderr, "Error getting signature length\n");
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    // 分配签名缓冲区
    *signature = malloc(*sig_len);
    if (!*signature) {
        fprintf(stderr, "Memory allocation failed\n");
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    // 生成签名
    size_t tmp_sig_len = *sig_len;
    if (EVP_DigestSign(ctx, *signature, &tmp_sig_len, message, message_len) <= 0) {
        fprintf(stderr, "Error creating signature\n");
        free(*signature);
        EVP_MD_CTX_free(ctx);
        return 0;
    }
    
    *sig_len = tmp_sig_len;
    EVP_MD_CTX_free(ctx);
    return 1;
}

/**
 * @brief 验证RSASSA-PSS签名
 * @param[in] public_key 公钥
 * @param[in] message 原始消息
 * @param[in] message_len 消息长度
 * @param[in] signature 待验证的签名
 * @param[in] sig_len 签名长度
 * @return 签名有效返回1,无效返回0,错误返回-1
 */
int rsassa_pss_verify(EVP_PKEY *public_key,
                     const unsigned char *message,
                     size_t message_len,
                     const unsigned char *signature,
                     size_t sig_len) {
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (!ctx) {
        fprintf(stderr, "Error creating EVP_MD_CTX\n");
        return -1;
    }
    
    // 初始化验证上下文
    EVP_PKEY_CTX *pkey_ctx = NULL;
    if (EVP_DigestVerifyInit(ctx, &pkey_ctx, EVP_sha256(), NULL, public_key) <= 0) {
        fprintf(stderr, "Error initializing digest verify\n");
        EVP_MD_CTX_free(ctx);
        return -1;
    }
    
    // 设置PSS填充参数
    if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) <= 0) {
        fprintf(stderr, "Error setting RSA PSS padding\n");
        EVP_MD_CTX_free(ctx);
        return -1;
    }
    
    // 设置盐长度(自动确定)
    if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO) <= 0) {
        fprintf(stderr, "Error setting salt length\n");
        EVP_MD_CTX_free(ctx);
        return -1;
    }
    
    // 验证签名
    int result = EVP_DigestVerify(ctx, signature, sig_len, message, message_len);
    
    EVP_MD_CTX_free(ctx);
    return result;
}

/**
 * @brief 将密钥保存到文件
 */
int save_key_to_file(const char *filename, EVP_PKEY *key, int is_private) {
    FILE *fp = fopen(filename, "w");
    if (!fp) {
        fprintf(stderr, "Error opening file %s\n", filename);
        return 0;
    }
    
    int result;
    if (is_private) {
        result = PEM_write_PrivateKey(fp, key, NULL, NULL, 0, NULL, NULL);
    } else {
        result = PEM_write_PUBKEY(fp, key);
    }
    
    fclose(fp);
    return (result != 0);
}

/**
 * @brief 从文件加载密钥
 */
EVP_PKEY *load_key_from_file(const char *filename, int is_private) {
    FILE *fp = fopen(filename, "r");
    if (!fp) {
        fprintf(stderr, "Error opening file %s\n", filename);
        return NULL;
    }
    
    EVP_PKEY *key;
    if (is_private) {
        key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
    } else {
        key = PEM_read_PUBKEY(fp, NULL, NULL, NULL);
    }
    
    fclose(fp);
    return key;
}

/**
 * @brief 主函数:演示RSASSA-PSS签名和验证
 */
int main() {
    printf("=== RSASSA-PSS签名和验证示例 ===\n\n");
    
    // 初始化OpenSSL
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    
    EVP_PKEY *private_key = NULL;
    EVP_PKEY *public_key = NULL;
    unsigned char *signature = NULL;
    size_t sig_len = 0;
    
    // 1. 生成RSA密钥对
    printf("1. 生成RSA密钥对 (%d位)...\n", RSA_KEY_BITS);
    if (!generate_rsa_keypair(RSA_KEY_BITS, &private_key, &public_key)) {
        fprintf(stderr, "密钥对生成失败\n");
        goto cleanup;
    }
    printf("   密钥对生成成功!\n\n");
    
    // 保存密钥到文件(可选)
    save_key_to_file("private_key.pem", private_key, 1);
    save_key_to_file("public_key.pem", public_key, 0);
    printf("   密钥已保存到文件: private_key.pem, public_key.pem\n\n");
    
    // 2. 准备消息
    const char *message = "这是一条使用RSASSA-PSS签名的测试消息";
    printf("2. 准备消息:\n");
    printf("   \"%s\"\n\n", message);
    
    // 3. 生成签名
    printf("3. 生成RSASSA-PSS签名...\n");
    if (!rsassa_pss_sign(private_key,
                        (const unsigned char *)message,
                        strlen(message),
                        &signature, &sig_len)) {
        fprintf(stderr, "签名生成失败\n");
        goto cleanup;
    }
    printf("   签名生成成功!\n");
    printf("   签名长度: %zu bytes\n\n", sig_len);
    
    // 保存签名到文件
    FILE *sig_file = fopen("message.sig", "wb");
    if (sig_file) {
        fwrite(signature, 1, sig_len, sig_file);
        fclose(sig_file);
        printf("   签名已保存到文件: message.sig\n\n");
    }
    
    // 4. 验证签名
    printf("4. 验证签名...\n");
    int verify_result = rsassa_pss_verify(public_key,
                                         (const unsigned char *)message,
                                         strlen(message),
                                         signature, sig_len);
    
    if (verify_result == 1) {
        printf("   ✓ 签名验证成功!\n\n");
    } else if (verify_result == 0) {
        printf("   ✗ 签名验证失败!\n\n");
    } else {
        printf("   ! 验证过程中发生错误\n\n");
    }
    
    // 5. 演示概率性签名
    printf("5. 演示PSS的概率性特性...\n");
    printf("   对同一消息进行两次签名:\n");
    
    unsigned char *signature2 = NULL;
    size_t sig_len2 = 0;
    
    if (rsassa_pss_sign(private_key,
                       (const unsigned char *)message,
                       strlen(message),
                       &signature2, &sig_len2)) {
        printf("   第一次签名: ");
        for (size_t i = 0; i < 8 && i < sig_len; i++) {
            printf("%02x", signature[i]);
        }
        printf("...\n");
        
        printf("   第二次签名: ");
        for (size_t i = 0; i < 8 && i < sig_len2; i++) {
            printf("%02x", signature2[i]);
        }
        printf("...\n");
        
        // 检查两个签名是否不同
        int signatures_different = 0;
        if (sig_len == sig_len2) {
            signatures_different = memcmp(signature, signature2, sig_len) != 0;
        } else {
            signatures_different = 1;
        }
        
        if (signatures_different) {
            printf("   ✓ 两个签名不同(概率性签名)\n");
        } else {
            printf("   ✗ 两个签名相同(意外情况)\n");
        }
        
        free(signature2);
    }
    printf("\n");
    
    // 6. 演示篡改检测
    printf("6. 演示篡改检测...\n");
    unsigned char *tampered_message = malloc(strlen(message) + 1);
    strcpy((char *)tampered_message, message);
    tampered_message[5] ^= 0xFF; // 篡改消息
    
    int tamper_result = rsassa_pss_verify(public_key,
                                         tampered_message,
                                         strlen(message),
                                         signature, sig_len);
    
    if (tamper_result == 1) {
        printf("   ✗ 篡改后的消息竟然验证通过了!\n");
    } else {
        printf("   ✓ 成功检测到消息被篡改!\n");
    }
    
    free(tampered_message);
    
cleanup:
    // 清理资源
    if (private_key) EVP_PKEY_free(private_key);
    if (public_key) EVP_PKEY_free(public_key);
    if (signature) free(signature);
    
    // 清理OpenSSL
    EVP_cleanup();
    ERR_free_strings();
    
    return 0;
}

4.4 性能对比程序

/**
 * @file comparison.c
 * @brief ECDSA与RSASSA-PSS性能对比
 * 
 * 编译命令: make comparison
 * 运行命令: ./comparison
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/ec.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/sha.h>

#define TEST_ITERATIONS 1000
#define MESSAGE_SIZE 1024

// 性能测试结构
typedef struct {
    const char *name;
    double keygen_time;
    double sign_time;
    double verify_time;
    size_t signature_size;
    size_t public_key_size;
    size_t private_key_size;
} benchmark_result_t;

/**
 * @brief 执行ECDSA性能测试
 */
benchmark_result_t benchmark_ecdsa() {
    benchmark_result_t result = {"ECDSA (P-256)", 0, 0, 0, 0, 0, 0};
    
    clock_t start, end;
    EC_KEY *private_key = NULL;
    EC_KEY *public_key = NULL;
    
    // 生成测试消息
    unsigned char message[MESSAGE_SIZE];
    RAND_bytes(message, MESSAGE_SIZE);
    
    // 1. 密钥生成性能
    printf("测试ECDSA密钥生成...\n");
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
        EC_KEY_generate_key(key);
        EC_KEY_free(key);
    }
    
    end = clock();
    result.keygen_time = ((double)(end - start)) / CLOCKS_PER_SEC / TEST_ITERATIONS * 1000;
    
    // 生成实际使用的密钥
    private_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_generate_key(private_key);
    
    // 计算密钥大小
    result.private_key_size = i2d_ECPrivateKey(private_key, NULL);
    
    // 获取公钥大小
    const EC_POINT *pub_point = EC_KEY_get0_public_key(private_key);
    public_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_public_key(public_key, pub_point);
    result.public_key_size = i2d_EC_PUBKEY(public_key, NULL);
    
    // 2. 签名性能
    printf("测试ECDSA签名...\n");
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(message, MESSAGE_SIZE, hash);
    
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        unsigned char *sig = NULL;
        unsigned int sig_len = 0;
        ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH, sig, &sig_len, private_key);
        free(sig);
    }
    
    end = clock();
    result.sign_time = ((double)(end - start)) / CLOCKS_PER_SEC / TEST_ITERATIONS * 1000;
    
    // 获取签名大小
    unsigned char *sig = NULL;
    unsigned int sig_len = 0;
    ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH, NULL, &sig_len, private_key);
    result.signature_size = sig_len;
    free(sig);
    
    // 3. 验证性能
    printf("测试ECDSA验证...\n");
    // 先生成一个签名用于验证
    sig = malloc(result.signature_size);
    ECDSA_sign(0, hash, SHA256_DIGEST_LENGTH, sig, &sig_len, private_key);
    
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        ECDSA_verify(0, hash, SHA256_DIGEST_LENGTH, sig, sig_len, public_key);
    }
    
    end = clock();
    result.verify_time = ((double)(end - start)) / CLOCKS_PER_SEC / TEST_ITERATIONS * 1000;
    
    // 清理
    free(sig);
    EC_KEY_free(private_key);
    EC_KEY_free(public_key);
    
    return result;
}

/**
 * @brief 执行RSA-PSS性能测试
 */
benchmark_result_t benchmark_rsa_pss(int rsa_bits) {
    char name[64];
    snprintf(name, sizeof(name), "RSA-PSS (%d位)", rsa_bits);
    benchmark_result_t result = {name, 0, 0, 0, 0, 0, 0};
    
    clock_t start, end;
    EVP_PKEY *private_key = NULL;
    EVP_PKEY *public_key = NULL;
    EVP_PKEY_CTX *ctx = NULL;
    
    // 生成测试消息
    unsigned char message[MESSAGE_SIZE];
    RAND_bytes(message, MESSAGE_SIZE);
    
    // 1. 密钥生成性能
    printf("测试RSA-%d密钥生成...\n", rsa_bits);
    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS / 10; i++) { // RSA密钥生成较慢,减少测试次数
        EVP_PKEY_CTX *tmp_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
        EVP_PKEY_keygen_init(tmp_ctx);
        EVP_PKEY_CTX_set_rsa_keygen_bits(tmp_ctx, rsa_bits);
        
        EVP_PKEY *tmp_key = NULL;
        EVP_PKEY_keygen(tmp_ctx, &tmp_key);
        
        EVP_PKEY_free(tmp_key);
        EVP_PKEY_CTX_free(tmp_ctx);
    }
    
    end = clock();
    result.keygen_time = ((double)(end - start)) / CLOCKS_PER_SEC / (TEST_ITERATIONS / 10) * 1000;
    
    // 生成实际使用的密钥
    EVP_PKEY_keygen_init(ctx);
    EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, rsa_bits);
    EVP_PKEY_keygen(ctx, &private_key);
    
    // 计算密钥大小
    result.private_key_size = i2d_PrivateKey(private_key, NULL);
    result.public_key_size = i2d_PublicKey(private_key, NULL);
    
    // 2. 签名性能
    printf("测试RSA-PSS签名...\n");
    EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
    EVP_PKEY_CTX *pkey_ctx = NULL;
    
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
        EVP_DigestSignInit(tmp_ctx, &pkey_ctx, EVP_sha256(), NULL, private_key);
        EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
        EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO);
        
        size_t sig_len = 0;
        EVP_DigestSign(tmp_ctx, NULL, &sig_len, message, MESSAGE_SIZE);
        
        EVP_MD_CTX_free(tmp_ctx);
    }
    
    end = clock();
    result.sign_time = ((double)(end - start)) / CLOCKS_PER_SEC / TEST_ITERATIONS * 1000;
    
    // 获取签名大小
    EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), NULL, private_key);
    EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
    EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO);
    
    size_t sig_len = 0;
    EVP_DigestSign(md_ctx, NULL, &sig_len, message, MESSAGE_SIZE);
    result.signature_size = sig_len;
    
    // 3. 验证性能
    printf("测试RSA-PSS验证...\n");
    // 先生成一个签名用于验证
    unsigned char *signature = malloc(sig_len);
    EVP_DigestSign(md_ctx, signature, &sig_len, message, MESSAGE_SIZE);
    
    start = clock();
    
    for (int i = 0; i < TEST_ITERATIONS; i++) {
        EVP_MD_CTX *tmp_ctx = EVP_MD_CTX_new();
        EVP_DigestVerifyInit(tmp_ctx, &pkey_ctx, EVP_sha256(), NULL, private_key);
        EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING);
        EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_AUTO);
        
        EVP_DigestVerify(tmp_ctx, signature, sig_len, message, MESSAGE_SIZE);
        
        EVP_MD_CTX_free(tmp_ctx);
    }
    
    end = clock();
    result.verify_time = ((double)(end - start)) / CLOCKS_PER_SEC / TEST_ITERATIONS * 1000;
    
    // 清理
    free(signature);
    EVP_MD_CTX_free(md_ctx);
    EVP_PKEY_free(private_key);
    EVP_PKEY_CTX_free(ctx);
    
    return result;
}

/**
 * @brief 打印性能结果
 */
void print_results(const benchmark_result_t *results, int count) {
    printf("\n" "="*70 "\n");
    printf("性能对比结果 (基于 %d 次迭代的平均值)\n", TEST_ITERATIONS);
    printf("="*70 "\n\n");
    
    printf("%-20s %12s %12s %12s %12s %12s %12s\n",
           "算法", "密钥生成(ms)", "签名(ms)", "验证(ms)", 
           "签名大小", "公钥大小", "私钥大小");
    printf("-"*70 "\n");
    
    for (int i = 0; i < count; i++) {
        printf("%-20s %12.3f %12.3f %12.3f %12zu %12zu %12zu\n",
               results[i].name,
               results[i].keygen_time,
               results[i].sign_time,
               results[i].verify_time,
               results[i].signature_size,
               results[i].public_key_size,
               results[i].private_key_size);
    }
    
    printf("\n说明:\n");
    printf("1. 时间单位: 毫秒(ms),越低越好\n");
    printf("2. 大小单位: 字节(bytes),越小越好\n");
    printf("3. ECDSA使用P-256曲线,提供约128位安全性\n");
    printf("4. RSA-2048提供约112位安全性,RSA-3072提供约128位安全性\n");
}

/**
 * @brief 打印推荐场景
 */
void print_recommendations() {
    printf("\n" "="*70 "\n");
    printf("算法选择推荐\n");
    printf("="*70 "\n\n");
    
    printf("推荐使用ECDSA的场景:\n");
    printf("  ✓ 移动设备和物联网(IoT)设备\n");
    printf("  ✓ 带宽受限的应用(如区块链、移动通信)\n");
    printf("  ✓ 需要短签名的场景(如证书链优化)\n");
    printf("  ✓ 需要高性能签名的服务器应用\n\n");
    
    printf("推荐使用RSASSA-PSS的场景:\n");
    printf("  ✓ 需要与传统系统兼容\n");
    printf("  ✓ 验证性能是关键需求的场景(如TLS握手)\n");
    printf("  ✓ 有硬件加速支持的环境(HSM、智能卡)\n");
    printf("  ✓ 需要可证明安全性的应用\n\n");
    
    printf("后量子密码学考虑:\n");
    printf("  ⚠  两种算法都易受量子计算攻击\n");
    printf("  💡 长期安全考虑应关注后量子密码算法:\n");
    printf("     - 基于格的签名(Dilithium)\n");
    printf("     - 基于哈希的签名(SPHINCS+)\n");
    printf("     - 基于多变量的签名\n");
}

/**
 * @brief 主函数
 */
int main() {
    printf("开始ECDSA与RSASSA-PSS性能对比测试...\n\n");
    
    // 初始化OpenSSL
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
    
    // 运行性能测试
    benchmark_result_t results[3];
    
    printf("测试 1/3: ECDSA (P-256)\n");
    results[0] = benchmark_ecdsa();
    
    printf("\n测试 2/3: RSA-PSS (2048位)\n");
    results[1] = benchmark_rsa_pss(2048);
    
    printf("\n测试 3/3: RSA-PSS (3072位)\n");
    results[2] = benchmark_rsa_pss(3072);
    
    // 打印结果
    print_results(results, 3);
    print_recommendations();
    
    // 清理OpenSSL
    EVP_cleanup();
    ERR_free_strings();
    
    return 0;
}

第五部分:总结与未来展望

5.1 技术总结:各有所长的双雄

ECDSA和RSASSA-PSS代表了现代数字签名的两个重要方向:一个追求效率和简洁,一个注重安全性和可证明性。

ECDSA的核心优势:

  • 密钥和签名尺寸小,适合带宽受限环境
  • 签名生成快,适合服务器端批量操作
  • 基于离散对数问题,后量子安全性略好于RSA

RSASSA-PSS的核心优势:

  • 验证速度极快,适合客户端验证场景
  • 可证明安全性(在随机预言机模型下)
  • 与传统基础设施兼容性好

5.2 应用实践建议

  1. TLS/SSL场景:

    • 服务器证书:优先考虑ECDSA(减少握手数据量)
    • 客户端验证:可考虑RSASSA-PSS(验证速度快)
  2. 区块链场景:

    • 比特币:ECDSA secp256k1是标准选择
    • 智能合约:考虑签名尺寸对gas费用的影响
  3. 嵌入式系统:

    • 资源极度受限:ECDSA P-256是理想选择
    • 有硬件加速:根据硬件支持选择

5.3 安全最佳实践

  1. 密钥管理:

    • 使用硬件安全模块(HSM)存储私钥
    • 定期轮换密钥,建立完善的密钥生命周期管理
  2. 随机数生成:

    • 使用密码学安全的随机数生成器
    • 对于ECDSA,确保每个签名的k值都是唯一的
  3. 算法参数选择:

    • ECDSA:使用标准化曲线(如P-256、secp256k1)
    • RSA:密钥长度至少2048位,推荐3072位以上

5.4 未来发展趋势

  1. 后量子密码学迁移:

    • 逐步部署抗量子算法
    • 考虑混合方案(如RSA+后量子签名)
  2. 标准化进展:

    • NIST后量子密码学标准化的推进
    • 新算法的互操作性测试
  3. 硬件加速:

    • 专用密码学处理器的发展
    • 量子安全算法的硬件实现优化

结语:密码学的永恒追求

数字签名算法的演进,是安全、效率、实用性之间不断平衡的艺术。ECDSA和RSASSA-PSS各自在不同的应用场景中发挥着重要作用,它们的设计哲学体现了密码学家的智慧:既要抵抗已知的攻击,又要预见未来的威胁。

正如现实世界中的签名不断演变一样,数字签名技术也必将继续发展。理解这些算法的原理、优势和局限,能帮助我们在数字世界的安全建设中做出明智的选择。


致读者:密码学是一个需要持续学习的领域。本文虽力求详尽,但技术仍在不断演进。建议您关注NIST、IETF等标准化组织的最新动态,以及密码学社区的研究进展。安全之路,永无止境。

附录:编译与运行说明

环境要求

  • 操作系统:Linux/macOS/Windows (WSL)
  • 编译器:GCC 或 Clang
  • OpenSSL库:版本1.1.1或更高
  • Make工具

编译步骤

# 1. 下载所有源代码文件
# 2. 确保Makefile在同一目录
# 3. 执行编译命令
make clean && make

# 或者单独编译
make ecdsa_example
make rsassa_pss_example
make comparison

运行示例

# 运行ECDSA示例
./ecdsa_example

# 运行RSA-PSS示例
./rsassa_pss_example

# 运行性能对比
./comparison

预期输出

每个程序都会显示详细的步骤和结果,包括:

  1. 密钥生成过程
  2. 签名生成和验证
  3. 性能数据(对比程序)
  4. 错误处理和安全性演示

故障排除

  1. OpenSSL库缺失:

    # Ubuntu/Debian
    sudo apt-get install libssl-dev
    
    # CentOS/RHEL
    sudo yum install openssl-devel
    
    # macOS
    brew install openssl
    
  2. 编译错误:

    • 检查OpenSSL版本
    • 确保所有头文件正确包含
  3. 运行错误:

    • 检查文件权限
    • 确保动态库路径正确

通过这个完整的解析,您应该对ECDSA和RSASSA-PSS有了全面而深入的理解。从数学原理到实际实现,从性能对比到应用选择,这两个算法代表了现代数字签名的精华所在。在日益数字化的世界中,掌握这些技术将为您的安全实践提供坚实的基础。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐