寻求mbed-TLS中双向TLS(Mutual TLS)的使用示例
mbed-TLS 双向TLS(Mutual TLS)嵌入式实现示例与实践经验
核心实现逻辑
双向TLS的核心是服务器和客户端互相验证对方证书,基于mbed-TLS的实现步骤统一,嵌入式场景下重点在于资源适配:
- 初始化mbed-TLS核心组件(SSL上下文、X509证书解析、随机数生成器)
- 强制开启双向认证模式(服务器要求客户端提供证书,客户端验证服务器证书)
- 双方各自加载自身证书链、私钥,以及用于验证对方的CA根证书
- 绑定嵌入式平台的网络IO函数,完成SSL握手与数据传输
嵌入式适配要点
- 内存优化:
- 在mbed-TLS配置头文件中关闭不必要的加密套件(比如只保留
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256这类轻量套件) - 降低TLS版本至TLS 1.2(避免TLS 1.3的额外内存开销)
- 禁用动态内存分配,改用静态内存池(配置
MBEDTLS_MEMORY_BUFFER_ALLOC_C)
- 在mbed-TLS配置头文件中关闭不必要的加密套件(比如只保留
- 存储适配:
- 证书和私钥预编译进Flash,读取时直接拷贝到RAM缓冲区,避免文件系统依赖
- 硬件加速:
- 若平台支持硬件加密(比如STM32的CRYP模块),启用对应的mbed-TLS硬件抽象层,大幅降低CPU占用
关键代码片段
服务器端核心实现
#include "mbedtls/ssl.h" #include "mbedtls/x509_crt.h" #include "mbedtls/pk.h" #include "mbedtls/net_sockets.h" #include "mbedtls/ctr_drbg.h" int main(void) { mbedtls_ssl_context ssl; mbedtls_ssl_config conf; mbedtls_x509_crt srvcert, cacert; mbedtls_pk_context pkey; mbedtls_net_ctx listen_fd, client_fd; mbedtls_ctr_drbg_context rng; int ret; // 初始化组件 mbedtls_net_init(&listen_fd); mbedtls_net_init(&client_fd); mbedtls_ssl_init(&ssl); mbedtls_ssl_config_init(&conf); mbedtls_x509_crt_init(&srvcert); mbedtls_x509_crt_init(&cacert); mbedtls_pk_init(&pkey); mbedtls_ctr_drbg_init(&rng); // 初始化随机数生成器(嵌入式需提供自定义熵源) mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, NULL, NULL, 0); // 配置双向认证:要求客户端必须提供有效证书 mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &rng); // 加载CA根证书(用于验证客户端证书) if ((ret = mbedtls_x509_crt_parse_file(&cacert, "/flash/ca.crt")) != 0) { // 错误处理:比如日志输出、复位 goto exit; } mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); // 加载服务器证书链和私钥 if ((ret = mbedtls_x509_crt_parse_file(&srvcert, "/flash/server.crt")) != 0 || (ret = mbedtls_pk_parse_keyfile(&pkey, "/flash/server.key", NULL, NULL, 0)) != 0) { goto exit; } mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey); // 绑定网络IO(嵌入式需实现自定义send/recv,此处用mbedtls_net示例) mbedtls_ssl_setup(&ssl, &conf); mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); // 执行SSL握手 while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { // 握手失败处理 goto exit; } } // 可选:额外校验客户端证书的字段(比如CN、有效期) if (mbedtls_ssl_get_verify_result(&ssl) != 0) { // 客户端证书验证不通过 mbedtls_ssl_close_notify(&ssl); goto exit; } // 后续数据收发:用mbedtls_ssl_read/write替代普通socket读写 uint8_t buf[1024]; while ((ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf))) > 0) { mbedtls_ssl_write(&ssl, buf, ret); // 回显示例 } exit: // 资源释放(嵌入式场景下需确保内存完全回收) mbedtls_ssl_free(&ssl); mbedtls_ssl_config_free(&conf); mbedtls_x509_crt_free(&srvcert); mbedtls_x509_crt_free(&cacert); mbedtls_pk_free(&pkey); mbedtls_net_free(&client_fd); mbedtls_net_free(&listen_fd); mbedtls_ctr_drbg_free(&rng); return ret; }
客户端核心实现
#include "mbedtls/ssl.h" #include "mbedtls/x509_crt.h" #include "mbedtls/pk.h" #include "mbedtls/net_sockets.h" #include "mbedtls/ctr_drbg.h" int main(void) { mbedtls_ssl_context ssl; mbedtls_ssl_config conf; mbedtls_x509_crt clicert, cacert; mbedtls_pk_context pkey; mbedtls_net_ctx server_fd; mbedtls_ctr_drbg_context rng; int ret; // 初始化组件 mbedtls_net_init(&server_fd); mbedtls_ssl_init(&ssl); mbedtls_ssl_config_init(&conf); mbedtls_x509_crt_init(&clicert); mbedtls_x509_crt_init(&cacert); mbedtls_pk_init(&pkey); mbedtls_ctr_drbg_init(&rng); // 初始化随机数生成器 mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, NULL, NULL, 0); // 配置客户端验证服务器证书 mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &rng); // 加载CA根证书(验证服务器证书) if ((ret = mbedtls_x509_crt_parse_file(&cacert, "/flash/ca.crt")) != 0) { goto exit; } mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL); // 加载客户端证书和私钥(用于服务器验证) if ((ret = mbedtls_x509_crt_parse_file(&clicert, "/flash/client.crt")) != 0 || (ret = mbedtls_pk_parse_keyfile(&pkey, "/flash/client.key", NULL, NULL, 0)) != 0) { goto exit; } mbedtls_ssl_conf_own_cert(&conf, &clicert, &pkey); // 连接服务器(嵌入式需替换为自定义网络连接逻辑) if ((ret = mbedtls_net_connect(&server_fd, "192.168.1.100", "443", MBEDTLS_NET_PROTO_TCP)) != 0) { goto exit; } // 绑定IO并设置SNI(可选,多域名服务器场景) mbedtls_ssl_setup(&ssl, &conf); mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); mbedtls_ssl_set_hostname(&ssl, "embedded-server.local"); // 执行握手 while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { goto exit; } } // 数据收发示例 uint8_t send_buf[] = "Hello Mutual TLS!"; mbedtls_ssl_write(&ssl, send_buf, sizeof(send_buf)-1); uint8_t recv_buf[1024]; ret = mbedtls_ssl_read(&ssl, recv_buf, sizeof(recv_buf)); if (ret > 0) { // 处理接收数据 } exit: // 资源释放 mbedtls_ssl_free(&ssl); mbedtls_ssl_config_free(&conf); mbedtls_x509_crt_free(&clicert); mbedtls_x509_crt_free(&cacert); mbedtls_pk_free(&pkey); mbedtls_net_free(&server_fd); mbedtls_ctr_drbg_free(&rng); return ret; }
常见问题排查
- 证书验证失败:检查CA证书是否正确、证书链是否完整、客户端/服务器证书是否由该CA签发
- 内存溢出:调整mbed-TLS编译配置,减少
MBEDTLS_SSL_MAX_FRAGMENT_LENGTH、关闭未使用的加密算法 - 握手超时:嵌入式网络延迟高,需增加
mbedtls_ssl_conf_read_timeout的超时值,正确处理WANT_READ/WANT_WRITE错误码(需重新调用握手函数)
内容的提问来源于stack exchange,提问作者PrismaDynamic
相关产品推荐
相关产品推荐

