返回首页

TLS/DTLS 安全通信学习笔记

本笔记学习 nRF SDK 中的 TLS(传输层安全)和 DTLS(数据报传输层安全)实现,用于加密网络通信。


示例路径

nrf/samples/cellular/tls_ciphersuites/  - TLS 密码套件测试
nrf/samples/crypto/psa_tls/             - PSA TLS 安全通道
zephyr/samples/net/secure_mqtt_sensor_actuator/ - 安全 MQTT 通信

核心概念

TLS vs DTLS

特性TLSDTLS 1.2
传输层TCPUDP
握手可靠传输不可靠传输
延迟较高较低
适用场景HTTP, MQTT, CoAPCoAP, LwM2M

TLS 握手流程

客户端                                  服务器
  |                                      |
  |------ ClientHello ------------------>|
  |                                      |
  |<----- ServerHello -------------------|
  |<----- Certificate ------------------|
  |<----- ServerKeyExchange ------------|
  |<----- ServerHelloDone --------------|
  |                                      |
  |------ ClientKeyExchange ----------->|
  |------ [ChangeCipherSpec] ----------->|
  |------ Finished --------------------->|
  |                                      |
  |<----- [ChangeCipherSpec] -----------|
  |<----- Finished ----------------------|
  |                                      |
  |<==== 加密数据传输 ====--------------|

nRF SDK TLS 实现

使用方式

方式说明适用
原生套接字 TLSSocket 选项设置通用
nRF 安全库nrf_security 库Nordic 芯片
PSA TLSPSA Crypto API需要 PSA 支持
modem TLS调制解调器内置nRF91 系列

原生套接字 TLS

配置

# prj.conf
CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_TLS_LIBRARY=y

# 证书存储
CONFIG_TLS_CREDENTIALS=y

头文件

#include <zephyr/net/tls_credentials.h>

注册证书

#include <zephyr/net/tls_credentials.h>

/* CA 证书(PEM 格式) */
static const char ca_cert[] =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIDXTCCAkWgAwIBAgIJAKLdQVPy90XxMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n"
    "..."
    "-----END CERTIFICATE-----\n";

/* 注册 CA 证书 */
static int cert_provision(void)
{
    int ret = tls_credential_add(
        42,                          /* 安全标签 (sec_tag) */
        TLS_CREDENTIAL_CA_CERTIFICATE,
        ca_cert,
        sizeof(ca_cert)
    );
    
    if (ret < 0) {
        printk("证书注册失败: %d\n", ret);
        return ret;
    }
    
    return 0;
}

TLS 客户端连接

#include <sys/socket.h>
#include <zephyr/net/tls_credentials.h>

#define SERVER_HOSTNAME "example.com"
#define SERVER_PORT 443
#define TLS_SEC_TAG 42

int tls_client_connect(void)
{
    int sock;
    struct sockaddr_in addr;
    struct hostent *host;
    
    /* 解析域名 */
    host = gethostbyname(SERVER_HOSTNAME);
    if (!host) {
        printk("DNS 查询失败\n");
        return -1;
    }
    
    /* 创建 TLS 套接字 */
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);
    if (sock < 0) {
        printk("创建套接字失败\n");
        return -1;
    }
    
    /* 设置 TLS 主机名(用于证书验证) */
    char host_buf[] = SERVER_HOSTNAME;
    setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
               host_buf, sizeof(host_buf));
    
    /* 设置使用的安全标签 */
    sec_tag_t sec_tag = TLS_SEC_TAG;
    setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
               &sec_tag, sizeof(sec_tag));
    
    /* 连接服务器 */
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
    
    int ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) {
        printk("连接失败: %d\n", errno);
        close(sock);
        return -1;
    }
    
    printk("TLS 连接成功!\n");
    return sock;
}

/* 发送 HTTPS 请求 */
void https_request(int sock)
{
    const char *request =
        "GET / HTTP/1.1\r\n"
        "Host: example.com\r\n"
        "Connection: close\r\n"
        "\r\n";
    
    send(sock, request, strlen(request), 0);
    
    /* 接收响应 */
    char response[512];
    int len = recv(sock, response, sizeof(response) - 1, 0);
    if (len > 0) {
        response[len] = '\0';
        printk("响应:\n%s\n", response);
    }
    
    close(sock);
}

DTLS 客户端(UDP 安全)

创建 DTLS 套接字

#include <sys/socket.h>
#include <zephyr/net/tls_credentials.h>

#define DTLS_SEC_TAG 43

int dtls_client_connect(void)
{
    int sock;
    struct sockaddr_in addr;
    
    /* 创建 DTLS 套接字 */
    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
    if (sock < 0) {
        printk("创建 DTLS 套接字失败\n");
        return -1;
    }
    
    /* 设置安全标签 */
    sec_tag_t sec_tag = DTLS_SEC_TAG;
    setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
               &sec_tag, sizeof(sec_tag));
    
    /* 设置 PSK(预共享密钥) */
    char psk[] = "my_pre_shared_key";
    char psk_id[] = "my_identity";
    
    /* 注册 PSK */
    tls_credential_add(DTLS_SEC_TAG,
                       TLS_CREDENTIAL_PSK,
                       psk, strlen(psk));
    tls_credential_add(DTLS_SEC_TAG,
                       TLS_CREDENTIAL_PSK_ID,
                       psk_id, strlen(psk_id));
    
    /* 连接服务器 */
    addr.sin_family = AF_INET;
    addr.sin_port = htons(5684);  /* CoAPS 默认端口 */
    inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);
    
    int ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) {
        printk("DTLS 连接失败: %d\n", errno);
        close(sock);
        return -1;
    }
    
    printk("DTLS 连接成功!\n");
    return sock;
}

/* 发送加密数据 */
void dtls_send(int sock, const char *data)
{
    send(sock, data, strlen(data), 0);
    
    char buffer[128];
    int len = recv(sock, buffer, sizeof(buffer), 0);
    if (len > 0) {
        buffer[len] = '\0';
        printk("DTLS 响应: %s\n", buffer);
    }
}

nRF 安全库 (nrf_security)

配置

# prj.conf
CONFIG_NRF_SECURITY=y
CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=8192

# TLS 协议版本
CONFIG_MBEDTLS_TLS_VERSION_1_2=y
CONFIG_MBEDTLS_TLS_VERSION_1_3=y

使用原生 Mbed TLS API

#include <mbedtls/ssl.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/x509_crt.h>

mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt cacert;

int mbedtls_init(void)
{
    mbedtls_ssl_init(&ssl);
    mbedtls_ssl_config_init(&conf);
    mbedtls_x509_crt_init(&cacert);
    
    /* 加载 CA 证书 */
    mbedtls_x509_crt_parse(&cacert, ca_cert, sizeof(ca_cert));
    
    /* 配置 SSL */
    mbedtls_ssl_config_defaults(&conf,
                                MBEDTLS_SSL_IS_CLIENT,
                                MBEDTLS_SSL_TRANSPORT_STREAM,
                                MBEDTLS_SSL_PRESET_DEFAULT);
    
    mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
    mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
    
    mbedtls_ssl_setup(&ssl, &conf);
    mbedtls_ssl_set_hostname(&ssl, "example.com");
    
    return 0;
}

TLS 密码套件配置

支持的密码套件

/* 常用密码套件 */
static const char cipher_suites[] =
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:"  /* 推荐 */
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:"    /* 推荐 */
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:"  /* 推荐 */
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:"    /* 推荐 */
    "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256:"    /* PSK */
    "TLS_PSK_WITH_AES_128_CCM_8";               /* CoAP/LwM2M */

/* 设置密码套件 */
setsockopt(sock, SOL_TLS, TLS_CIPHERSUITE_LIST,
           cipher_suites, strlen(cipher_suites));

nRF91 系列 Modem TLS

调制解调器内置 TLS

#include <modem/nrf_modem_lib.h>
#include <modem/modem_key_mgmt.h>

/* 初始化调制解调器 */
nrf_modem_lib_init();

/* 使用 modem_key_mgmt 库存储证书 */
modem_key_mgmt_write(
    42,                          /* sec_tag */
    MODEM_KEY_MGMT_CRED_TYPE_CA_CHAIN,
    ca_cert,
    sizeof(ca_cert)
);

/* 套接字使用调制解调器 TLS */
int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);

完整示例:HTTPS 客户端

#include <zephyr/kernel.h>
#include <sys/socket.h>
#include <zephyr/net/tls_credentials.h>

#define HTTPS_HOSTNAME "api.example.com"
#define HTTPS_PORT 443
#define TLS_SEC_TAG 100

static const char ca_cert[] = {
#include "DigiCertGlobalG2.pem.inc"
};

int https_client(void)
{
    int sock, ret;
    struct sockaddr_in addr;
    
    /* 注册证书 */
    ret = tls_credential_add(TLS_SEC_TAG,
                              TLS_CREDENTIAL_CA_CERTIFICATE,
                              ca_cert, sizeof(ca_cert));
    if (ret < 0) {
        printk("证书注册失败: %d\n", ret);
        return ret;
    }
    
    /* 创建 TLS 套接字 */
    sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2);
    if (sock < 0) {
        printk("创建套接字失败\n");
        return -errno;
    }
    
    /* 设置 TLS 选项 */
    char hostname[] = HTTPS_HOSTNAME;
    setsockopt(sock, SOL_TLS, TLS_HOSTNAME,
               hostname, strlen(hostname) + 1);
    
    sec_tag_t sec_tag = TLS_SEC_TAG;
    setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST,
               &sec_tag, sizeof(sec_tag));
    
    /* 连接服务器 */
    addr.sin_family = AF_INET;
    addr.sin_port = htons(HTTPS_PORT);
    inet_pton(AF_INET, "93.184.216.34", &addr.sin_addr);
    
    ret = connect(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret < 0) {
        printk("连接失败: %d\n", errno);
        close(sock);
        return -errno;
    }
    
    printk("TLS 握手成功!\n");
    
    /* 发送请求 */
    const char *req = "GET /api/data HTTP/1.1\r\n"
                      "Host: api.example.com\r\n"
                      "Accept: application/json\r\n"
                      "\r\n";
    send(sock, req, strlen(req), 0);
    
    /* 接收响应 */
    char buf[1024];
    ret = recv(sock, buf, sizeof(buf) - 1, 0);
    if (ret > 0) {
        buf[ret] = '\0';
        printk("响应: %s\n", buf);
    }
    
    close(sock);
    return 0;
}

配置选项

# 基础 TLS 配置
CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
CONFIG_TLS_CREDENTIALS=y
CONFIG_MBEDTLS=y
CONFIG_MBEDTLS_TLS_LIBRARY=y

# 协议版本
CONFIG_MBEDTLS_TLS_VERSION_1_2=y
CONFIG_MBEDTLS_TLS_VERSION_1_3=y

# nRF Security(推荐)
CONFIG_NRF_SECURITY=y
CONFIG_MBEDTLS_HEAP_SIZE=8192

# 调试
CONFIG_MBEDTLS_DEBUG=y
CONFIG_MBEDTLS_DEBUG_LEVEL=3

错误处理

错误码含义
EIOTLS 握手失败
EINVAL参数无效
ENOTCONN未连接
ECONNRESET连接重置

安全最佳实践

  1. 验证服务器证书: 始终启用 MBEDTLS_SSL_VERIFY_REQUIRED
  2. 使用强密码套件: 禁用弱算法(如 RC4, DES)
  3. 证书有效期检查: 启用证书过期验证
  4. 最小化协议版本: 使用 TLS 1.2 或更高

相关 API 总结

函数说明
socket(AF_INET, SOCK_STREAM, IPPROTO_TLS_1_2)创建 TLS 套接字
tls_credential_add()注册证书/密钥
setsockopt(sock, SOL_TLS, TLS_HOSTNAME)设置主机名
setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST)设置安全标签
connect()执行 TLS 握手

*小白 🤖 - 2026-03-17*