为什么你的物联网设备需要加密?如何将MbedTLS库移植到STM32等嵌入式平台?本文将为你详细解答。
一、为什么要移植MbedTLS?------背景篇
1.1 物联网的安全困境
在SSL/TLS协议出现之前,很多应用层协议(HTTP、FTP、MQTT等)都面临着严峻的网络安全问题:
- 信息泄露:HTTP等协议使用明文传输,报文一旦被截获便会泄露传输内容
- 数据篡改:传输过程中报文如果被篡改,接收方无法察觉
- 身份伪造:无法保证通信对端身份的真实性
你可能会问:这些和物联网设备有什么关系?
想象一下:你的智能门锁、智能摄像头、工业传感器通过明文传输数据------攻击者可以轻松截获开锁指令、窃取敏感数据、甚至冒充服务器下发恶意指令。这不仅是隐私问题,更是安全隐患。
1.2 MbedTLS是什么?
Mbed TLS是一个开源的、可移植的、轻量级的SSL/TLS库,使用标准C语言编写。它的核心特点包括:
| 特性 | 说明 |
|---|---|
| 轻量级 | 代码占用空间小,适合资源受限的嵌入式设备 |
| 模块化 | 独立模块设计,可按需裁剪功能 |
| 功能完整 | 包含加密库、X.509证书操作、SSL/TLS和DTLS协议 |
| 开源许可 | Apache 2.0许可证,商业友好 |
1.3 有了MbedTLS能做什么?
将MbedTLS库移植到你的设备后,可以实现:
TCP + TLS = TCP(S) → 安全的TCP通信
MQTT + TLS = MQTT(S) → 安全的MQTT连接(物联网常用)
HTTP + TLS = HTTPS → 安全的HTTP请求
COAP + DTLS = COAP(S) → 安全的COAP协议
简单来说:MbedTLS为你的设备穿上了"安全铠甲"。
1.4 为什么不用OpenSSL?
OpenSSL功能更强大,但体量也更大。对于资源受限的嵌入式设备(如只有几十KB RAM的MCU),MbedTLS是更合适的选择:
| 对比项 | OpenSSL | MbedTLS |
|---|---|---|
| 代码体积 | 较大(MB级别) | 小巧(KB级别) |
| 资源消耗 | 高 | 低 |
| 配置灵活性 | 一般 | 极高(宏定义裁剪) |
| 适用场景 | 服务器、PC | 嵌入式设备、IoT |
二、下载MbedTLS库------获取篇
2.1 官方下载方式
Mbed TLS的官方源码托管在GitHub上。打开终端,执行以下命令:
bash
# 克隆完整仓库(包含子模块)
git clone --recurse-submodules https://github.com/Mbed-TLS/mbedtls.git
# 或者指定版本(推荐)
git clone --recurse-submodules --branch v3.6.0 https://github.com/Mbed-TLS/mbedtls.git
如果只需要源码包,也可以访问GitHub Release页面下载压缩包:
- 仓库地址:https://github.com/ARMmbed/mbedtls
- 选择需要的版本,下载ZIP或tar.gz文件
2.2 源码结构解析
下载后,你会看到以下核心目录:
mbedtls/
├── include/mbedtls/ # 头文件(API声明)
│ ├── config.h # 核心配置文件(重要!)
│ ├── ssl.h # SSL/TLS API
│ ├── sha1.h # 哈希算法API
│ └── ...
├── library/ # 源码实现(.c文件)
├── programs/ # 示例程序
│ └── ssl/ # SSL示例(ssl_client2.c等)
└── tests/ # 测试代码
重点关注 :include/mbedtls/config.h 是整个库的"开关面板",通过宏定义控制编译哪些功能。
三、移植MbedTLS库------实战篇
3.1 移植的本质
MbedTLS在电脑上可以直接运行,但在嵌入式平台上,需要替换三部分内容:
┌─────────────────────────────────────────┐
│ MbedTLS 需要平台提供三个能力: │
├─────────────────────────────────────────┤
│ 1. 网络接口 - 数据的收发能力 │
│ 2. 内存管理 - malloc/free能力 │
│ 3. 定时器 - 时间计算能力(DTLS需要) │
└─────────────────────────────────────────┘
3.2 方法一:使用STM32CubeMX(STM32芯片专用)
如果你的芯片是STM32且使用STM32CubeMX,这是最简单的方法。
步骤1:准备基础工程
先创建一个可以正常使用printf打印的工程,确保串口能输出信息。
步骤2:开启RNG外设(可选)
一些STM32系列有硬件随机数发生器(RNG),开启它可以增强安全性。没有的话也没关系,MbedTLS会用软件实现。
步骤3:在Middleware中开启mbedtls
在STM32CubeMX的中间件选项中找到mbedtls,勾选启用。
步骤4:配置需要的功能模块
MbedTLS提供了大量加密算法,你可以通过勾选/取消勾选来决定编译哪些模块。原则是:只选你需要的,以节省资源。
步骤5:生成代码并测试
生成工程后,在main.c中添加测试代码:
c
#include "mbedtls/sha1.h"
#include "string.h"
// 测试SHA1加密
char *source = "hello mbedtls";
char encrypt[64];
printf("原文: %s\r\n", source);
mbedtls_sha1_context ctx;
mbedtls_sha1_init(&ctx);
mbedtls_sha1_starts(&ctx);
mbedtls_sha1_update(&ctx, (unsigned char *)source, strlen(source));
mbedtls_sha1_finish(&ctx, (unsigned char *)encrypt);
mbedtls_sha1_free(&ctx);
printf("SHA1加密结果: ");
for(int i = 0; i < 20; i++) {
printf("%02x", (unsigned char)encrypt[i]);
}
printf("\r\n");
注意:Keil-MDK需要改为ANSI编码,否则字符串编码问题会导致加密结果出错。
3.3 方法二:手动移植(通用方法)
不使用CubeMX或使用其他MCU时,可采用手动移植。
步骤1:复制源码文件
将mbedtls的library文件夹中所有.c文件复制到你的工程中。
步骤2:复制头文件
将include/mbedtls文件夹复制到工程的头文件路径中。
步骤3:复制配置文件
从源码根目录复制一份config.h(或示例配置文件)到工程中,方便后续修改。
步骤4:在工程中添加文件
以Keil-MDK为例:
- 将library中的所有
.c文件添加到工程 - 添加头文件路径
- 在编译宏中指定配置文件:
MBEDTLS_CONFIG_FILE=<config.h>
步骤5:按需裁剪配置
编辑config.h,通过注释/取消注释宏定义来控制功能:
c
// 启用需要的功能
#define MBEDTLS_SHA1_C // SHA1算法
#define MBEDTLS_AES_C // AES加密
#define MBEDTLS_SSL_TLS_C // SSL/TLS协议
// 禁用不需要的功能(节省空间)
// #define MBEDTLS_MD5_C // 如果不需要MD5就注释掉
3.4 平台适配:替换底层接口
这是移植的核心环节。MbedTLS需要你的平台提供三个底层能力:
3.4.1 网络接口适配
MbedTLS默认的网络接口是阻塞式的,适用于电脑端。在嵌入式系统中,你需要自己实现:
c
// 需要实现的网络接口
int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto);
int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len);
int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len);
void mbedtls_net_free(mbedtls_net_context *ctx);
实现后,通过mbedtls_ssl_set_bio注册给MbedTLS:
c
mbedtls_ssl_set_bio(&ssl, &server_fd,
custom_send, // 你的发送函数
custom_recv, // 你的接收函数
custom_recv_timeout);
3.4.2 内存管理适配
如果你的系统有自己的内存管理,可以替换默认的malloc/free:
c
// 注册自定义内存管理函数
mbedtls_platform_set_calloc_free(custom_calloc, custom_free);
3.4.3 定时器适配(DTLS需要)
如果使用DTLS(UDP版本的TLS),需要实现定时器接口:
c
// 定时器回调
void platform_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms);
int platform_timing_get_delay(void *data);
// 注册给MbedTLS
mbedtls_ssl_set_timer_cb(&ssl, &timer_ctx,
platform_timing_set_delay,
platform_timing_get_delay);
3.4.4 随机数适配
MbedTLS需要熵源(随机数)来生成密钥。如果你的芯片有硬件随机数发生器(RNG),可以实现:
c
// 硬件随机数接口
int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t *olen);
并在配置中定义:
c
#define MBEDTLS_ENTROPY_HARDWARE_ALT
四、常见问题与解决
Q1:编译报错,说某个功能需要依赖其他功能?
在 MbedTLS 的配置文件(mbedtls_config.h,适用于 3.0 及以上版本)中,系统会自动检查各个配置宏之间的依赖关系。如果配置不完整,编译器会给出提示,你只需要根据报错信息,把缺失的依赖宏打开即可。
需要注意的是:这些宏开关中,大部分控制的是库本身的功能,而有一小部分则决定了某些功能是使用库的实现,还是交给平台去完成。
其中,以 ALT 结尾的宏就属于后者。它们的作用是告诉 MbedTLS:
"不要用我默认的软件实现,换成我自己(用户)提供的硬件或定制版本。"
例如,当你打开 MBEDTLS_AES_ALT 时,MbedTLS 就不再提供自带的 AES 实现,而是期望你在外部实现相关接口。
Q2:提示找不到time.h或网络相关头文件?
在配置文件中添加:
c
#define MBEDTLS_NO_PLATFORM_ENTROPY // 没有平台熵源时
Q3:我的设备没有文件系统,如何加载证书?
嵌入式设备通常将证书以数组形式存储,而不是文件:
c
// 将证书文件转为数组
const unsigned char my_cert[] = {
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, ... // 证书内容
};
// 使用内存方式加载
mbedtls_x509_crt_parse(&cacert, my_cert, sizeof(my_cert));
五、移植总结与建议
移植MbedTLS的整体思路可以概括为:
┌─────────────────────────────────────────────────┐
│ 1. 获取源码 → 2. 添加文件 → 3. 裁剪配置 │
│ ↓ │
│ 4. 适配底层(网络/内存/定时器/随机数) │
│ ↓ │
│ 5. 编写测试代码验证 → 完成! │
└─────────────────────────────────────────────────┘
关键原则:
- 按需裁剪:只开启你需要的加密算法和协议特性,这能显著减少代码体积和内存占用
- 善用配置检查:MbedTLS自带依赖检查,编译错误时根据提示补充宏定义即可
- 从简单开始:先测试纯算法功能(如SHA1),再逐步集成网络功能