DTLS(Datagram Transport Layer Security)作为TLS的UDP适配版本,为基于UDP的应用(如CoAP、MQTT-SN等)提供了安全保障。在资源受限的嵌入式系统中,如何选择和集成一个合适的轻量级安全库至关重要。
轻量级TLS/DTLS库选择
| 库名称 |
特点 |
适用场景 |
| mbed TLS |
轻量级、可配置性强、支持TLS 1.3 |
资源受限的IoT设备 |
| WolfSSL |
高性能、支持硬件加速、开源 |
对性能要求较高的设备 |
| OpenSSL |
功能完整、广泛使用 |
资源充足的Linux网关 |
| TinyDTLS |
专为DTLS优化、极小内存占用 |
仅需DTLS的极简设备 |
mbed TLS集成
mbed TLS(原PolarSSL)是ARM公司开发的开源加密库,专为嵌入式系统设计,具有轻量级、可配置性强的特点。
基本集成步骤
-
获取源码
git clone https://github.com/Mbed-TLS/mbedtls
-
配置构建选项
-
编译库文件
make
-
集成到项目中
- 添加头文件路径:
-I/path/to/mbedtls/include
- 链接库文件:
-L/path/to/mbedtls/build/library -lmbedtls -lmbedcrypto -lmbedx509
最小化配置示例
对于存储和计算资源都紧张的设备,可以通过自定义 mbedtls_config.h 来裁剪功能,这是优化的核心步骤。
/* mbedtls_config.h - 适用于3.x版本 */
#ifndef MBEDTLS_CONFIG_H
#define MBEDTLS_CONFIG_H
/* 平台支持 */
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_PLATFORM_MEMORY
#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4
/* 随机数生成 */
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_FORCE_SHA256 // 节省ROM,强制使用SHA256
/* 加密算法(仅保留必要算法) */
#define MBEDTLS_AES_C
#define MBEDTLS_AES_ROM_TABLES // 查表法提速,牺牲一点ROM
#define MBEDTLS_CCM_C // 用于CoAP加密
#define MBEDTLS_GCM_C // 可选,用于高性能场景
#define MBEDTLS_SHA256_C // 必须,用于证书验证
#define MBEDTLS_SHA256_SMALLER // 节省ROM的慢速实现
/* TLS/DTLS核心 */
#define MBEDTLS_SSL_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_SSL_PROTO_TLS1_3 // 如需TLS 1.3则开启(增加约50KB)
#define MBEDTLS_SSL_DTLS_ANTI_REPLAY // DTLS防重放攻击必需
#define MBEDTLS_SSL_DTLS_HELLO_VERIFY // DTLS防DoS必需
#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE // 支持NAT环境
#define MBEDTLS_SSL_EXPORT_KEYS // 如需调试密钥
/* X509证书处理 */
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_RSA_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED // 仅保留P-256曲线,节省空间
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECDSA_C
/* 密钥交换套件(至少选一种) */
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED // 无证书模式
#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED // PSK+前向安全
/* 自定义内存限制(运行时配置,非宏定义) */
#define MBEDTLS_MEMORY_DEBUG 0
#include "mbedtls/check_config.h"
#endif /* MBEDTLS_CONFIG_H */
证书管理
证书格式与存储
- 证书格式:X.509 DER或PEM格式
- 存储方式:
- 内置到固件中(RO flash)
- 存储在外部闪存/EEPROM
- 使用安全元件(SE)存储
证书压缩与优化
- 使用最小化证书:仅包含必要的扩展
- 证书链优化:只包含必要的中间证书
- 使用椭圆曲线证书:比RSA证书小得多
- 预验证证书:在编译时验证,减少运行时开销
密钥交换优化
密钥交换算法选择
| 算法 |
优势 |
劣势 |
适用场景 |
| RSA |
广泛支持 |
密钥尺寸大,计算慢 |
兼容性要求高的场景 |
| ECDHE |
密钥尺寸小,计算快 |
某些旧设备不支持 |
资源受限设备 |
| PSK |
无证书,握手快 |
需要预共享密钥 |
封闭系统 |
| ECDHE_PSK |
结合ECDHE和PSK优点 |
实现复杂度增加 |
平衡安全和性能 |
预共享密钥(PSK)配置
PSK模式无需证书,可以极大简化握手流程并降低开销,非常适合设备已知的封闭系统。
// mbed TLS PSK配置示例
static const unsigned char psk[] = "my_pre_shared_key";
static const unsigned char psk_id[] = "device_identifier";
mbedtls_ssl_conf_psk(&conf, psk, sizeof(psk) - 1, psk_id, sizeof(psk_id) - 1);
会话恢复机制
- 会话ID:服务器为每个会话分配唯一ID
- 会话票据:服务器生成加密的会话信息
// 启用会话恢复
mbedtls_ssl_conf_session_cache(&conf, &session_cache, mbedtls_ssl_session_save, mbedtls_ssl_session_load);
实现示例
基础TLS客户端实现
下面是一个完整的、可用于嵌入式环境的mbed TLS客户端示例。这段C语言代码清晰地展示了初始化和握手的完整流程。
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h" /* 注意:不是 net.h */
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/x509.h"
#include "mbedtls/error.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/* 嵌入真实CA证书(PEM格式) - 替换为实际证书内容 */
static const char *ca_certificate =
"-----BEGIN CERTIFICATE-----\n"
"MIIDSjCCAjKgAwIBAgIQRK+YG+2P1J+B4CQSHAEzmDANBgkqhkiG9w0BAQsFADBH\n"
"MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\n"
"QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy\n"
"...\n"
"-----END CERTIFICATE-----\n";
typedef struct {
mbedtls_net_context net;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt cacert;
} tls_context_t;
static int tls_init(tls_context_t *ctx, const char *pers)
{
int ret;
mbedtls_net_init(&ctx->net);
mbedtls_ssl_init(&ctx->ssl);
mbedtls_ssl_config_init(&ctx->conf);
mbedtls_entropy_init(&ctx->entropy);
mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
mbedtls_x509_crt_init(&ctx->cacert);
/* 初始化随机数生成器 */
ret = mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func,
&ctx->entropy,
(const unsigned char *)pers, strlen(pers));
if (ret != 0) {
printf("Random seed failed: 0x%04X\n", -ret);
return ret;
}
/* 解析CA证书 */
ret = mbedtls_x509_crt_parse(&ctx->cacert,
(const unsigned char *)ca_certificate,
strlen(ca_certificate) + 1);
if (ret < 0) {
printf("CA parse failed: 0x%04X\n", -ret);
goto cleanup;
}
return 0;
cleanup:
mbedtls_x509_crt_free(&ctx->cacert);
mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
mbedtls_entropy_free(&ctx->entropy);
return ret;
}
static int tls_connect(tls_context_t *ctx, const char *host, const char *port)
{
int ret;
/* 1. 配置默认值(客户端,流模式) */
ret = mbedtls_ssl_config_defaults(&ctx->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, /* TCP */
MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
printf("Config defaults failed: 0x%04X\n", -ret);
return ret;
}
/* 2. 设置证书验证 */
mbedtls_ssl_conf_authmode(&ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&ctx->conf, &ctx->cacert, NULL);
mbedtls_ssl_conf_rng(&ctx->conf, mbedtls_ctr_drbg_random, &ctx->ctr_drbg);
/* 3. 设置SNI(Server Name Indication) */
ret = mbedtls_ssl_set_hostname(&ctx->ssl, host);
if (ret != 0) {
printf("Set hostname failed: 0x%04X\n", -ret);
return ret;
}
/* 4. 应用配置到SSL上下文 */
ret = mbedtls_ssl_setup(&ctx->ssl, &ctx->conf);
if (ret != 0) {
printf("SSL setup failed: 0x%04X\n", -ret);
return ret;
}
/* 5. 建立TCP连接(port必须是字符串) */
ret = mbedtls_net_connect(&ctx->net, host, port, MBEDTLS_NET_PROTO_TCP);
if (ret != 0) {
printf("TCP connect failed: 0x%04X\n", -ret);
return ret;
}
/* 6. 绑定网络IO */
mbedtls_ssl_set_bio(&ctx->ssl, &ctx->net,
mbedtls_net_send, mbedtls_net_recv, NULL);
/* 7. 执行SSL握手(支持非阻塞,此处用阻塞简化) */
printf("Starting TLS handshake...\n");
while ((ret = mbedtls_ssl_handshake(&ctx->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
printf("Handshake failed: 0x%04X\n", -ret);
mbedtls_ssl_session_print(mbedtls_ssl_get_session_pointer(&ctx->ssl));
return ret;
}
}
/* 8. 验证证书 */
uint32_t flags;
if ((flags = mbedtls_ssl_get_verify_result(&ctx->ssl)) != 0) {
char vrfy_buf[512];
mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", flags);
printf("Certificate verification failed:\n%s\n", vrfy_buf);
return -1;
}
printf("TLS connection established, cipher: %s\n",
mbedtls_ssl_get_ciphersuite(&ctx->ssl));
return 0;
}
static int tls_send(tls_context_t *ctx, const uint8_t *data, size_t len)
{
int ret;
do {
ret = mbedtls_ssl_write(&ctx->ssl, data, len);
} while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE);
return ret;
}
static int tls_recv(tls_context_t *ctx, uint8_t *buf, size_t len)
{
int ret;
do {
ret = mbedtls_ssl_read(&ctx->ssl, buf, len);
} while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE);
return ret;
}
static void tls_close(tls_context_t *ctx)
{
/* 发送close notify */
mbedtls_ssl_close_notify(&ctx->ssl);
/* 释放资源 */
mbedtls_net_free(&ctx->net);
mbedtls_x509_crt_free(&ctx->cacert);
mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
mbedtls_entropy_free(&ctx->entropy);
mbedtls_ssl_config_free(&ctx->conf);
mbedtls_ssl_free(&ctx->ssl);
}
/* 使用示例 */
int tls_client_example(void)
{
tls_context_t ctx;
int ret;
ret = tls_init(&ctx, "tls_client_demo");
if (ret != 0) return ret;
ret = tls_connect(&ctx, "example.com", "443"); /* port是字符串 */
if (ret == 0) {
const char *req = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
tls_send(&ctx, (const uint8_t *)req, strlen(req));
uint8_t buf[1024];
ret = tls_recv(&ctx, buf, sizeof(buf) - 1);
if (ret > 0) {
buf[ret] = '\0';
printf("Received: %s\n", buf);
}
}
tls_close(&ctx);
return ret;
}
DTLS客户端实现
DTLS在握手流程上针对UDP的不可靠性增加了防重放和防DoS机制,代码结构与TLS类似,但配置时需要指定数据报传输模式。
#include "mbedtls/net.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/certs.h"
int dtls_client_example(const char *server_addr, int port) {
int ret;
mbedtls_net_context net_ctx;
mbedtls_ssl_context ssl_ctx;
mbedtls_ssl_config ssl_conf;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt ca_cert;
// 初始化
mbedtls_net_init(&net_ctx);
mbedtls_ssl_init(&ssl_ctx);
mbedtls_ssl_config_init(&ssl_conf);
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_x509_crt_init(&ca_cert);
// 种子随机数生成器
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
(const unsigned char *)"dtls_client", 11);
if (ret != 0) {
return ret;
}
// 加载CA证书
ret = mbedtls_x509_crt_parse(&ca_cert, (const unsigned char *)mbedtls_test_cas_pem,
mbedtls_test_cas_pem_len);
if (ret != 0) {
return ret;
}
// 配置SSL(使用UDP传输)
ret = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_DATAGRAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
return ret;
}
// 设置CA证书
mbedtls_ssl_conf_ca_chain(&ssl_conf, &ca_cert, NULL);
mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &ctr_drbg);
// 关联SSL上下文和配置
ret = mbedtls_ssl_setup(&ssl_ctx, &ssl_conf);
if (ret != 0) {
return ret;
}
// 设置主机名
mbedtls_ssl_set_hostname(&ssl_ctx, "example.com");
// 建立UDP连接
ret = mbedtls_net_connect(&net_ctx, server_addr, port, MBEDTLS_NET_PROTO_UDP);
if (ret != 0) {
return ret;
}
// 设置SSL BIO
mbedtls_ssl_set_bio(&ssl_ctx, &net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL);
// 执行DTLS握手
while ((ret = mbedtls_ssl_handshake(&ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
return ret;
}
}
// 发送数据
const char *message = "Hello, DTLS!";
ret = mbedtls_ssl_write(&ssl_ctx, (const unsigned char *)message, strlen(message));
if (ret < 0) {
return ret;
}
// 接收数据
unsigned char buffer[1024];
ret = mbedtls_ssl_read(&ssl_ctx, buffer, sizeof(buffer) - 1);
if (ret < 0) {
return ret;
}
buffer[ret] = '\0';
// 关闭连接
mbedtls_ssl_close_notify(&ssl_ctx);
mbedtls_net_free(&net_ctx);
// 释放资源
mbedtls_x509_crt_free(&ca_cert);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
mbedtls_ssl_config_free(&ssl_conf);
mbedtls_ssl_free(&ssl_ctx);
return 0;
}
总结
在嵌入式设备上实现TLS/DTLS安全通信,关键在于根据资源约束进行合理的选型和裁剪。mbed TLS凭借其高度的模块化和可配置性,成为许多IoT项目的首选。通过精细化的配置、证书优化以及选择合适的密钥交换方式,完全可以在保证安全性的同时,满足嵌入式设备对ROM、RAM和性能的苛刻要求。希望本文提供的配置示例和代码能为你项目中的安全通信集成带来切实帮助。如果你在实践过程中遇到其他问题,欢迎到云栈社区的技术论坛与更多开发者交流探讨。