STM32 平台 RSA 签名 / 验证 C 语言实现(基于 HAL 库 + mbed TLS)
STM32 (如 F1/F4/L4 系列)具备更强的算力和内存,适配 mbed TLS 库更流畅,以下实现 从机签名 + 主机验证 完整流程,基于 STM32 HAL 库,支持 RSA 2048/4096 位,可直接整合到 STM32 主机 / 从机程序。
一、前置准备
1. 硬件与软件环境
硬件:任意 STM32 开发板(如 STM32F407、STM32L476);
软件:STM32CubeIDE(或 Keil MDK)、STM32 HAL 库、mbed TLS 2.28.0(LTS 版本);
依赖:STM32 需启用串口(日志打印)、Flash(可选,存储密钥)。
2. mbed TLS 库移植(STM32 适配)
(1)下载与裁剪库
同 nRF51822 移植步骤,下载 mbed TLS 2.28.0,保留核心文件:
mbedtls-2.28.0/
├── include/ // 全部头文件
├── library/ // 核心源码(rsa.c、sha256.c、bignum.c、md.c、platform.c)
└── configs/ // 配置文件(选用 config-mini-tls1_2.h)
2)STM32CubeIDE 工程配置
新建 STM32 工程(启用 HAL 库),在工程根目录创建 Middlewares/mbedtls 文件夹,复制上述文件;
添加头文件路径:Project -> Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU GCC Compiler -> Include paths,添加:
${ProjDirPath}/Middlewares/mbedtls/include
${ProjDirPath}/Middlewares/mbedtls/configs
3.添加源文件:在 Src 目录下创建 mbedtls_sources.cmake,指定需编译的 mbed TLS 源码(或直接在 IDE 中添加 library 下的 .c 文件);
4.定义宏(Project -> Properties -> C/C++ Build -> Settings -> Tool Settings -> MCU GCC Compiler -> Preprocessor):
MBEDTLS_CONFIG_FILE="config-mini-tls1_2.h"
MBEDTLS_NO_PLATFORM_ENTROPY
MBEDTLS_ENTROPY_C
MBEDTLS_RSA_C
MBEDTLS_SHA256_C
MBEDTLS_BIGNUM_C
STM32_HAL_SUPPORT // 适配 STM32 HAL 库
(3)STM32 平台适配(修改 platform.c)
适配 STM32 的内存分配、日志打印(基于 HAL 库串口):
#include "stm32f4xx_hal.h"
#include "usart.h" // 假设已初始化 USART1(日志打印用)
// 替换 mbed TLS 内存分配函数(用标准库 malloc/free)
void *mbedtls_platform_malloc(size_t size)
{
return malloc(size);
}
void mbedtls_platform_free(void *ptr)
{
free(ptr);
}
// 替换 printf(用 STM32 HAL 串口打印)
int mbedtls_platform_snprintf(char *buf, size_t len, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int ret = vsnprintf(buf, len, fmt, args);
va_end(args);
return ret;
}
// 日志打印函数(串口输出,供 mbed TLS 调试)
void mbedtls_printf(const char *fmt, ...)
{
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
HAL_UART_Transmit(&huart1, (uint8_t *)buf, strlen(buf), HAL_MAX_DELAY);
}
二、核心实现:RSA 签名(STM32 从机侧)
1. 密钥配置与全局变量
#include "mbedtls/rsa.h"
#include "mbedtls/sha256.h"
#include "mbedtls/md.h"
// RSA 配置(2048 位,STM32 可支持 4096 位)
#define RSA_KEY_BITS 2048
#define RSA_SIGN_LEN (RSA_KEY_BITS / 8) // 2048 位 → 256 字节签名
// 从机私钥(PEM 转 C 数组,用 OpenSSL 生成,示例为占位符)
static const uint8_t m_rsa_private_key[] = {
0x30, 0x82, 0x04, 0xA2, 0x02, 0x01, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A,
// ... 省略完整私钥数据(约 500 字节)...
};
// 全局上下文
static mbedtls_rsa_context m_rsa_ctx;
static uint8_t m_signature[RSA_SIGN_LEN]; // 签名结果缓冲区
2. RSA 初始化(加载私钥)
/**
* @brief RSA 初始化(仅上电执行一次)
* @return HAL_StatusTypeDef: 成功返回 HAL_OK
*/
HAL_StatusTypeDef rsa_slave_init(void)
{
int mbed_err;
// 初始化 RSA 上下文(PKCS#1 v1.5 填充,SHA-256 哈希)
mbedtls_rsa_init(&m_rsa_ctx, MBEDTLS_RSA_PKCS_V15, 0);
mbedtls_rsa_set_padding(&m_rsa_ctx, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256);
// 加载私钥(PEM 格式解析)
mbed_err = mbedtls_pk_parse_key(&m_rsa_ctx.pk,
m_rsa_private_key, sizeof(m_rsa_private_key),
NULL, 0); // 无密码保护
if (mbed_err != 0)
{
mbedtls_printf("RSA load private key err: %drn", mbed_err);
return HAL_ERROR;
}
// 验证私钥有效性
mbed_err = mbedtls_rsa_check_private_key(&m_rsa_ctx);
if (mbed_err != 0)
{
mbedtls_printf("RSA private key invalid: %drn", mbed_err);
return HAL_ERROR;
}
mbedtls_printf("RSA slave init success (2048 bits)rn");
return HAL_OK;
}
3. RSA 签名函数(对数据生成签名)
/**
* @brief 对数据做 RSA 签名(SHA-256 哈希 + 私钥签名)
* @param p_data: 待签名数据(如传感器数据、指令)
* @param data_len: 数据长度
* @param p_signature: 输出签名结果(长度 = RSA_SIGN_LEN)
* @return HAL_StatusTypeDef: 成功返回 HAL_OK
*/
HAL_StatusTypeDef rsa_sign(uint8_t *p_data, uint16_t data_len, uint8_t *p_signature)
{
int mbed_err;
mbedtls_md_context_t md_ctx;
uint8_t hash[MBEDTLS_MD_SHA256_HASH_SIZE]; // SHA-256 哈希结果(32 字节)
// 初始化哈希上下文
mbedtls_md_init(&md_ctx);
mbed_err = mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0);
if (mbed_err != 0)
{
mbedtls_printf("SHA-256 setup err: %drn", mbed_err);
return HAL_ERROR;
}
// 1. 计算数据 SHA-256 哈希
mbedtls_md_starts(&md_ctx);
mbedtls_md_update(&md_ctx, p_data, data_len);
mbedtls_md_finish(&md_ctx, hash);
// 2. RSA 私钥签名
mbed_err = mbedtls_rsa_sign(&m_rsa_ctx, NULL, NULL,
MBEDTLS_MD_SHA256, MBEDTLS_MD_SHA256_HASH_SIZE,
hash, p_signature);
if (mbed_err != 0)
{
mbedtls_printf("RSA sign err: %drn", mbed_err);
return HAL_ERROR;
}
// 释放资源
mbedtls_md_free(&md_ctx);
mbedtls_printf("RSA sign success! Data len: %drn", data_len);
return HAL_OK;
}
4. 应用示例:发送带签名的数据
// 示例:通过串口/BLE 发送「原始数据 + 签名」
void send_data_with_sign(void)
{
// 1. 待发送原始数据
uint8_t raw_data[] = "STM32_Slave_Data_2024";
uint16_t raw_len = sizeof(raw_data);
// 2. 生成 RSA 签名
if (rsa_sign(raw_data, raw_len, m_signature) != HAL_OK)
{
mbedtls_printf("Sign failed, cancel sendrn");
return;
}
// 3. 拼接数据(原始数据 + 签名)
uint8_t send_buf[raw_len + RSA_SIGN_LEN];
memcpy(send_buf, raw_data, raw_len);
memcpy(send_buf + raw_len, m_signature, RSA_SIGN_LEN);
uint16_t send_len = raw_len + RSA_SIGN_LEN;
// 4. 发送(示例用串口,可替换为 BLE、SPI 等)
HAL_UART_Transmit(&huart1, send_buf, send_len, HAL_MAX_DELAY);
mbedtls_printf("Send data+sign: total len = %drn", send_len);
}
三、核心实现:RSA 验证(STM32 主机侧)
主机用从机公开的公钥验证签名,确认数据完整性和身份合法性。
1. 主机公钥配置
// 从机公钥(PEM 转 C 数组,从私钥提取,示例为占位符)
static const uint8_t m_rsa_public_key[] = {
0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
// ... 省略完整公钥数据(约 200 字节)...
};
// 主机 RSA 上下文
static mbedtls_rsa_context m_rsa_host_ctx;
2. 主机 RSA 初始化(加载公钥)
HAL_StatusTypeDef rsa_host_init(void)
{
int mbed_err;
// 初始化 RSA 上下文(验证模式)
mbedtls_rsa_init(&m_rsa_host_ctx, MBEDTLS_RSA_PKCS_V15, 0);
mbedtls_rsa_set_padding(&m_rsa_host_ctx, MBEDTLS_RSA_PKCS_V15, MBEDTLS_MD_SHA256);
// 加载公钥
mbed_err = mbedtls_pk_parse_public_key(&m_rsa_host_ctx.pk,
m_rsa_public_key, sizeof(m_rsa_public_key));
if (mbed_err != 0)
{
mbedtls_printf("RSA load public key err: %drn", mbed_err);
return HAL_ERROR;
}
mbedtls_printf("RSA host init successrn");
return HAL_OK;
}
3. RSA 验证函数
/**
* @brief 验证数据签名(SHA-256 哈希 + 公钥验证)
* @param p_data: 接收的原始数据
* @param data_len: 原始数据长度
* @param p_signature: 接收的签名数据
* @return bool: true=验证通过,false=验证失败
*/
bool rsa_verify(uint8_t *p_data, uint16_t data_len, uint8_t *p_signature)
{
int mbed_err;
mbedtls_md_context_t md_ctx;
uint8_t hash[MBEDTLS_MD_SHA256_HASH_SIZE];
// 初始化哈希上下文
mbedtls_md_init(&md_ctx);
mbed_err = mbedtls_md_setup(&md_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0);
if (mbed_err != 0)
{
mbedtls_printf("SHA-256 setup err: %drn", mbed_err);
return false;
}
// 1. 计算接收数据的 SHA-256 哈希
mbedtls_md_starts(&md_ctx);
mbedtls_md_update(&md_ctx, p_data, data_len);
mbedtls_md_finish(&md_ctx, hash);
// 2. 用公钥验证签名
mbed_err = mbedtls_rsa_verify(&m_rsa_host_ctx, NULL, NULL,
MBEDTLS_MD_SHA256, MBEDTLS_MD_SHA256_HASH_SIZE,
hash, p_signature);
if (mbed_err != 0)
{
mbedtls_printf("RSA verify failed! Err: %drn", mbed_err);
return false;
}
// 释放资源
mbedtls_md_free(&md_ctx);
mbedtls_printf("RSA verify success! Data is validrn");
return true;
}
4. 应用示例:接收并验证数据
// 示例:串口接收数据并验证签名
#define RECV_BUF_SIZE 512
uint8_t recv_buf[RECV_BUF_SIZE];
uint16_t recv_len = 0;
void receive_and_verify(void)
{
// 1. 接收数据(假设已通过串口接收「原始数据 + 签名」)
// HAL_UART_Receive(&huart1, recv_buf, RECV_BUF_SIZE, HAL_MAX_DELAY);
// 此处简化:假设 recv_buf 已存储接收数据,recv_len 为总长度
// 2. 分离原始数据和签名(需提前约定原始数据长度,或在数据中添加长度字段)
uint16_t raw_len = recv_len - RSA_SIGN_LEN; // 总长度 - 签名长度 = 原始数据长度
uint8_t *p_raw_data = recv_buf;
uint8_t *p_signature = recv_buf + raw_len;
// 3. 验证签名
if (rsa_verify(p_raw_data, raw_len, p_signature))
{
// 验证通过,处理数据
mbedtls_printf("Process valid data: %.*srn", raw_len, p_raw_data);
}
else
{
// 验证失败,丢弃数据
mbedtls_printf("Invalid data, discardrn");
}
}
四、密钥生成工具(OpenSSL)
用 OpenSSL 生成密钥对并转换为 C 数组:
# 1. 生成 2048 位私钥
openssl genrsa -out private_key.pem 2048
# 2. 提取公钥
openssl rsa -in private_key.pem -pubout -out public_key.pem
# 3. 转换为 C 数组(用 Python 脚本 pem2c.py)
python pem2c.py private_key.pem > private_key.h
python pem2c.py public_key.pem > public_key.h
五、STM32 适配优化
算力优化:STM32F4/L4 支持硬件浮点运算,可在 mbed TLS 中启用 MBEDTLS_HAVE_ASM 宏,加速 RSA 大整数运算;
内存管理:若 RAM 不足,可减小 config-mini-tls1_2.h 中的 MBEDTLS_MPI_MAX_SIZE(2048 位 RSA 需至少 256 字节);
密钥存储:公钥可直接嵌入代码,私钥建议存储在 STM32 的 Flash 加密区域(如 STM32L4 的 Option Bytes),避免泄露;
中断安全:RSA 运算耗时较长(2048 位约 100~500ms),建议在非中断上下文执行,或启用 DMA 减少 CPU 占用。
537