4、非对称加密算法移植(RSA/ECC):大数运算库移植、密钥生成与存储、性能瓶颈分析

非对称加密,说白了就是公钥和私钥这对「钥匙扣」。RSA 和 ECC 是其中最常用的两种。把它们移植到嵌入式设备上,可不是简单复制代码就完事。我做过好几个项目,每次都在大数运算和密钥存储上栽过跟头。今天咱们就聊聊这些坑怎么填。

4.1 大数运算库移植:别小看这步

RSA 和 ECC 的核心,都是大数运算。嵌入式芯片通常只有 32 位或 64 位整数运算能力,而 RSA 需要处理 1024 位甚至 2048 位的数。怎么办?得靠大数运算库。

我个人习惯用 MiraclOpenSSL 的 BIGNUM。但直接移植 OpenSSL 到单片机?太臃肿了。我建议用 Mbed TLS 或者 WolfSSL,它们专门为嵌入式优化过。

移植要点:

  • 内存管理:大数运算需要动态分配内存。嵌入式环境里,malloc/free 可能很慢,甚至没有。我建议用静态内存池,提前分配好最大尺寸的缓冲区。
  • 字节序:大端还是小端?ARM 默认小端,但 RSA 标准用大端。移植时一定要做转换。我曾经因为字节序搞反,调试了整整两天。
  • 优化乘法:大数乘法是性能瓶颈。用 Karatsuba 算法或 Toom-Cook 算法能快不少。但注意,这些算法对内存有额外要求。
// 示例:Mbed TLS 中大数初始化和乘法
#include "mbedtls/bignum.h"

mbedtls_mpi a, b, result;
mbedtls_mpi_init(&a);
mbedtls_mpi_init(&b);
mbedtls_mpi_init(&result);

// 赋值:0x1234567890ABCDEF
mbedtls_mpi_read_string(&a, 16, "1234567890ABCDEF");
mbedtls_mpi_read_string(&b, 16, "FEDCBA0987654321");

// 乘法
mbedtls_mpi_mul_mpi(&result, &a, &b);

// 打印结果
char buf[512];
mbedtls_mpi_write_string(&result, 16, buf, sizeof(buf), NULL);
printf("Result: %s\n", buf);

mbedtls_mpi_free(&a);
mbedtls_mpi_free(&b);
mbedtls_mpi_free(&result);

我的经验: 在 STM32F4 上,用 Mbed TLS 做一次 2048 位 RSA 签名,大约需要 200ms。如果换成纯软件实现,可能要 2 秒以上。所以,选对库很重要。

4.2 密钥生成与存储:安全第一

密钥生成,说白了就是找两个大素数(RSA)或者一个随机数(ECC)。但嵌入式设备随机数质量往往不行。我遇到过用 rand() 生成密钥的案例,结果密钥被轻松猜出来。

正确的做法:

  • 使用硬件随机数生成器(TRNG)。大多数现代 MCU 都有。
  • 如果没有,用软件熵源,比如 ADC 噪声、时钟抖动。
  • 生成后一定要做 素性检测(RSA)或 点验证(ECC)。

避坑指南: 我曾经在某个项目里,直接用软件生成的随机数做 ECC 密钥。结果因为随机数质量差,生成的公钥居然落在了弱曲线上。攻击者轻松算出了私钥。从那以后,我每次生成密钥都会做一次「密钥对一致性测试」。

密钥存储:

私钥不能明文存 Flash。我建议用以下方式:

  1. 加密存储:用设备唯一 ID 或用户口令加密私钥。
  2. 安全元件:如果成本允许,用 ATECC608A 这类专用芯片。
  3. OTP 区域:一次性可编程区域,防止被读出。
存储方式 安全性 成本 适用场景
Flash 明文 仅用于测试
Flash 加密 大多数消费类产品
安全元件 金融、车规
OTP 一次性烧录

4.3 性能瓶颈分析:哪里最慢?

非对称加密在嵌入式上,慢是常态。但到底慢在哪?我拿 RSA-2048 签名做个分析:

  • 模幂运算:占了 90% 以上的时间。说白了就是 a^b mod n
  • 素性检测:生成密钥时,找素数很慢。Miller-Rabin 算法要跑很多轮。
  • 点乘运算:ECC 里,k * G 是核心。用 Montgomery 阶梯法能防侧信道攻击,但会慢一些。

优化建议:

  • 使用硬件加速器:很多 MCU 内置 RSA/ECC 协处理器。比如 NXP LPC55xx 系列。
  • 预计算:对于固定公钥,可以预计算一些中间结果。比如 ECC 的固定点乘。
  • 选择合适曲线:ECC 用 Curve25519 比 P-256 快很多,而且更安全。
  • 减少模运算次数:用 Barrett 约减或 Montgomery 约减代替直接取模。
// 示例:使用硬件加速器(伪代码)
// 假设 MCU 有 RSA 硬件模块
void rsa_sign_hw(const uint8_t *hash, uint8_t *signature) {
    // 1. 将数据加载到硬件寄存器
    RSA_LoadMessage(hash);
    RSA_LoadPrivateKey(&private_key);
    
    // 2. 启动硬件运算
    RSA_StartSign();
    
    // 3. 等待完成(通常用中断或轮询)
    while (!RSA_IsDone());
    
    // 4. 读取结果
    RSA_ReadSignature(signature);
}

我的经验: 在 STM32H7 上,用硬件 RSA 加速器做一次签名只需要 5ms,而纯软件需要 200ms。差了 40 倍!所以,选 MCU 时一定要看有没有硬件加速。

嗯,这里要注意一点:硬件加速器虽然快,但往往只支持特定算法。比如有些只支持 RSA-1024,不支持 RSA-2048。选型时一定要确认清楚。

最后,我想说:非对称加密移植,不是简单的「复制-粘贴-编译」。你得理解大数运算的原理,知道密钥怎么安全存储,还得会分析性能瓶颈。我见过太多项目,因为忽略这些细节,导致产品上市后出现安全漏洞或性能问题。希望今天的分享,能帮你少走一些弯路。