# SSL/TLS ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 功能概述 本章节介绍SSL/TLS协议的基本概念、功能特点和应用场景,帮助用户理解SSL在嵌入式/物联网场景中的应用。 ## 简介 SSL/TLS功能模块提供统一的加密通信接口,支持多种SSL/TLS协议和加密算法,提供安全的数据传输功能。该模块支持证书验证、会话恢复等高级特性。 SSL(Secure Sockets Layer)和TLS(Transport Layer Security)是用于在计算机网络中提供通信安全性的加密协议。它们位于应用层协议(如HTTP、SMTP、MQTT)和传输层协议(如TCP)之间,为数据传输提供加密、数据完整性和身份验证。 ## 功能特点 - **多协议支持**:支持TLS和DTLS协议 - **灵活的认证方式**:支持证书认证和PSK认证 - **证书管理**:支持CA证书、客户端证书的加载和验证 - **会话管理**:支持会话恢复和复用 - **非阻塞模式**:支持异步IO操作 - **错误处理**:提供详细的错误码和错误信息 - **安全特性**:支持证书验证、主机名验证等 ## 应用场景 - **HTTPS安全通信**:为Web应用提供加密通信 - **MQTT over TLS**:物联网设备的安全MQTT通信 - **安全的WebSocket连接**:实时通信的安全保障 - **DTLS安全UDP通信**:基于UDP的安全通信 - **其他需要加密通信的场景**:如FTP over TLS、邮件加密传输等 # SSL API ## 头文件 *qosa_def.h* *qosa_sys.h* *qosa_queue_list.h* *qcm_vtls_cfg.h* ## 函数概览 | **函数** | **描述** | | --- | --- | | *qcm_ssl_init()* | 初始化SSL/TLS模块 | | *qcm_ssl_new()* | 创建SSL连接对象 | | *qcm_ssl_free()* | 用于回收qcm_ssl_new申请的资源 | | *qcm_ssl_connect()* | 发送SSL链接接口,阻塞式接口 | | *qcm_ssl_connect_nonblocking()* | 发送SSL链接接口,非阻塞式接口 | | *qcm_ssl_close()* | SSL资源释放关闭 | | *qcm_ssl_read()* | SSL连接成功后数据读取接口 | | *qcm_ssl_write()* | SSL连接成功后数据发送接口 | | *qcm_ssl_add_sessionid()* | 设置SSL session会话保存 | | *qcm_ssl_get_session()* | 获取SSL session会话ID | | *qcm_ssl_clean_all_sessionid()* | 清空全部SSL session会话 | | *qcm_ssl_version()* | 获取SSL版本相关信息 | | *qcm_ssl_data_pending()* | 获取SSL缓存数据信息状态 | | *qcm_ssl_check_ciphersuit_is_valid()* | 检索对应的ciphersuit算法是否有效 | | *qcm_ssl_set_hostinfo()* | 如果开启SNI或者session,则必须配置hostinfo连接信息,用以保存host主机名称以及对应的端口号 | | *qcm_ssl_set_cacertex_path()* | 设置全局SSL使用的CA证书 | | *qcm_ssl_get_cacertex_path()* | 获取全局对应配置的CA证书路径 | ## 函数详解 本章节详细说明SSL/TLS功能模块提供的API,包括初始化、连接管理、数据传输、会话管理以及辅助接口。 ### 初始化和配置API #### qcm_ssl_init - **功能描述** VTLS相关功能初始化调用,用于初始化内部配置管理信息,只需要初始化一次即可,可重复调用。 - **函数原型** ```c int qcm_ssl_init(void) ``` - **参数说明** 无 - **返回值说明** *1*:函数执行成功 *0*:函数执行失败 ```{note} 1. 在调用任何其他SSL函数之前,必须至少调用一次此函数。 2. 该函数初始化SSL模块的全局资源,多次调用效果相同。 3. 建议在程序启动时调用一次,在程序退出时不需要显式调用去初始化函数。 ``` **示例** ```c int main(void) { if (qcm_ssl_init() == 0) { printf("SSL初始化失败\n"); return -1; } /* 使用SSL功能... */ return 0; } ``` #### qcm_ssl_new - **功能描述** 创建SSL连接对象,针对 *qcm_ssl_config_t* 可以根据需要自行配置对应所要启用的SSL能力。 - **函数原型** ```c qcm_ssl_connect_data_t *qcm_ssl_new(qcm_ssl_config_t *ssl_config_ptr) ``` - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *ssl_config_ptr* | 输入 | *qcm_ssl_config_t** | 用户自定义SSL配置 | - **返回值说明** 返回对应申请地址:函数执行成功 *OSA_NULL*:函数执行失败 ```{note} 1. 调用此函数前必须确保已调用 ***qcm_ssl_init()*** 进行初始化。 2. 返回的指针需要在使用完毕后通过 ***qcm_ssl_free()*** 释放。 3. 配置结构体中的字段应根据实际使用场景进行设置,不使用的字段可以设置为 *NULL* 或 *0*。 ``` - **示例** ```c int main(void) { qcm_ssl_config_t ssl_config = {0}; qcm_ssl_connect_data_t *ssl_conn = NULL; // 配置SSL参数 ssl_config.ssl_version = QCM_SSL_VERSION_3; // TLS 1.2 ssl_config.transport = QCM_SSL_TLS_PROTOCOL; ssl_config.auth_mode = QCM_SSL_VERIFY_SERVER; if (qcm_ssl_init() == 0) { return -1; } ssl_conn = qcm_ssl_new(&ssl_config); if (ssl_conn == NULL) { printf("创建SSL连接对象失败\n"); return -1; } // 使用SSL连接... qcm_ssl_free(ssl_conn); return 0; } ``` #### qcm_ssl_free - **函数原型** ```c void qcm_ssl_free(qcm_ssl_connect_data_t *connssl) ``` - **功能描述** 用于回收 *qcm_ssl_new()* 申请的资源。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接地址指针 | - **返回值说明** 无 ```{note} 1. 调用之前需要先执行 ***qcm_ssl_close()***,释放SSL会话。 2. 释放后的指针不应再被使用。 3. 对NULL指针调用此函数是安全的。 ``` **示例** ```c void cleanup_ssl(qcm_ssl_connect_data_t *ssl_conn) { if (ssl_conn != NULL) { qcm_ssl_close(ssl_conn); qcm_ssl_free(ssl_conn); } } ``` ### 连接管理API #### qcm_ssl_connect - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_connect(qcm_ssl_connect_data_t *connssl) ``` - **功能描述** 发送SSL链接接口和阻塞式接口,退出即可检查对应SSL链接状态。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL内部链接配置指针 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 此函数为阻塞式调用,在SSL握手完成或超时前不会返回。 2. 调用前需要确保已正确配置SSL连接对象。 3. 建议设置合理的超时时间,避免长时间阻塞。 ``` - **示例** ```c int establish_ssl_connection(qcm_ssl_connect_data_t *ssl_conn) { qcm_vtls_result_status_e result; result = qcm_ssl_connect(ssl_conn); if (result != QCM_VTLS_RESULT_OK) { printf("SSL连接失败,错误码: %d\n", result); return -1; } printf("SSL连接成功建立\n"); return 0; } ``` #### qcm_ssl_connect_nonblocking - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_connect_nonblocking(qcm_ssl_connect_data_t *connssl, qosa_bool_t *done); ``` - **功能描述** 发送SSL链接接口,非阻塞式接口。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入/输出 | *qcm_ssl_connect_data_t ** | SSL连接对象指针 | | *done* | 输出 | qosa_bool_t * | 用来告知上层握手是否完成;取值范围:0~1 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 此函数为非阻塞式调用,需要多次调用直到握手完成。 2. 当*done为 *QOSA_TRUE* 时,表示握手完成。 3. 在非阻塞模式下,需要配合 ***qcm_ssl_data_pending()*** 等函数使用。 ``` - **示例** ```c int nonblocking_ssl_connect(qcm_ssl_connect_data_t *ssl_conn) { qcm_vtls_result_status_e result; qosa_bool_t done = QOSA_FALSE; int retry_count = 0; while (!done && retry_count < 100) { result = qcm_ssl_connect_nonblocking(ssl_conn, &done); if (result != QCM_VTLS_RESULT_OK && result != QCM_VTLS_SSL_READ_WRITE_EAGAIN) { printf("非阻塞SSL连接失败,错误码: %d\n", result); return -1; } if (!done) { // 等待一段时间后重试 qosa_sleep(100); retry_count++; } } if (done) { printf("非阻塞SSL连接成功建立\n"); return 0; } else { printf("非阻塞SSL连接超时\n"); return -1; } } ``` #### qcm_ssl_close - **函数原型** ```c void qcm_ssl_close(qcm_ssl_connect_data_t *connssl) ``` - **功能描述** SSL资源释放关闭。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接对象指针 | - **返回值说明** 无 ```{note} 1. 关闭SSL连接后,底层socket连接可能仍然保持打开状态。 2. 建议在关闭SSL连接后也关闭底层socket连接。 3. 对NULL指针调用此函数是安全的。 ``` - **示例** ```c void close_ssl_connection(qcm_ssl_connect_data_t *ssl_conn, int socket_fd) { if (ssl_conn != NULL) { qcm_ssl_close(ssl_conn); } if (socket_fd >= 0) { close(socket_fd); } } ``` ### 数据传输API #### qcm_ssl_read - **函数原型** ```c qosa_size_t qcm_ssl_read(qcm_ssl_connect_data_t *connssl, char *buf, qosa_size_t buffersize, qcm_vtls_result_status_e *curlcode) ``` - **功能描述** SSL连接成功后数据读取接口。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | connssl | 输入 | *qcm_ssl_connect_data_t ** | 对应ssl内部链接指针;范围为有效连接对象指针 | | buf | 输出 | char * | read读取数据内容指针;范围为有效缓冲区指针 | | buffersize | 输入 | qosa_size_t | 对应buff空间大小,需大于0 | | curlcode | 输出 | *qcm_vtls_result_status_e ** | 错误码;范围为有效指针 | - **返回值说明** 返回对应实际读取到的数量大小。 ```{note} 1. 读取的数据可能少于请求的缓冲区大小。 2. 当返回 *0* 且错误码为 *QCM_VTLS_SSL_PEER_CLOSE_NOTIFY_ERR* 时,表示对端已关闭连接。 3. 在非阻塞模式下,可能需要多次调用才能读取完整数据。 ``` - **示例** ```c int read_ssl_data(qcm_ssl_connect_data_t *ssl_conn, char *buffer, size_t buffer_size) { qcm_vtls_result_status_e error_code; qosa_size_t bytes_read; bytes_read = qcm_ssl_read(ssl_conn, buffer, buffer_size, &error_code); if (error_code != QCM_VTLS_RESULT_OK) { if (error_code == QCM_VTLS_SSL_PEER_CLOSE_NOTIFY_ERR) { printf("对端已关闭SSL连接\n"); } else { printf("读取SSL数据失败,错误码: %d\n", error_code); } return -1; } printf("成功读取%zu字节数据\n", bytes_read); return bytes_read; } ``` #### qcm_ssl_write - **函数原型** ```c qosa_size_t qcm_ssl_write(qcm_ssl_connect_data_t *connssl, char *buf, qosa_size_t buffersize, qcm_vtls_result_status_e *curlcode) ``` - **功能描述** 向SSL连接写入数据。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接对象指针;范围为有效连接对象指针 | | *buf* | 输入 | char * | write写数据内容指针;范围为有效数据指针 | | *buffersize* | 输入 | qosa_size_t | 对应buff内部的实际数据长度;需大于0 | | *curlcode* | 输出 | *qcm_vtls_result_status_e ** | 错误码;范围为有效指针 | - **返回值说明** 返回对应实际写入的数量大小。 ```{note} 1. 写入的数据可能少于请求的数据大小。 2. 在非阻塞模式下,可能需要多次调用才能写入完整数据。 3. 建议检查返回值,确保所有数据都已成功写入。 ``` - **示例** ```c int write_ssl_data(qcm_ssl_connect_data_t *ssl_conn, const char *data, size_t data_len) { qcm_vtls_result_status_e error_code; qosa_size_t total_written = 0; qosa_size_t bytes_written; while (total_written < data_len) { bytes_written = qcm_ssl_write(ssl_conn, (char *)data + total_written, data_len - total_written, &error_code); if (error_code != QCM_VTLS_RESULT_OK && error_code != QCM_VTLS_SSL_READ_WRITE_EAGAIN) { printf("写入SSL数据失败,错误码: %d\n", error_code); return -1; } if (bytes_written > 0) { total_written += bytes_written; } if (total_written < data_len) { // 等待可写事件 qosa_sleep(10); } } printf("成功写入%zu字节数据\n", total_written); return total_written; } ``` ### 会话管理API #### qcm_ssl_add_sessionid - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_add_sessionid(qcm_ssl_connect_data_t *connssl, void *ssl_sessionid) ``` - **功能描述** 设置SSL session会话保存。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接对象指针,范围为有效连接对象指针 | | *ssl_sessionid* | 输入 | void * | SSL会话结构体指针;范围为有效的结构体指针 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 会话恢复可以显著提高后续SSL连接的速度。 2. 保存的会话信息应在后续连接中尽快使用。 3. 会话信息可能因服务器配置而过期。 ``` - **示例** ```c int save_ssl_session(qcm_ssl_connect_data_t *ssl_conn, void *session_data) { qcm_vtls_result_status_e result; result = qcm_ssl_add_sessionid(ssl_conn, session_data); if (result != QCM_VTLS_RESULT_OK) { printf("保存SSL会话失败,错误码: %d\n", result); return -1; } printf("SSL会话保存成功\n"); return 0; } ``` #### qcm_ssl_get_sessionid - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_get_sessionid(qcm_ssl_connect_data_t *connssl, void **ssl_sessionid); ``` - **功能描述** 获取SSL session会话ID。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接对象指针;范围为有效连接对象指针 | | *ssl_sessionid* | 输出 | void ** | 获取对应的ssl session地址;范围为有效双重指针 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 获取的会话信息可用于后续连接的快速恢复。 2. 使用完毕后需要适当管理会话内存。 3. 会话信息可能因超时或服务器策略而失效。 ``` - **示例** ```c int get_ssl_session(qcm_ssl_connect_data_t *ssl_conn, void **session_ptr) { qcm_vtls_result_status_e result; result = qcm_ssl_get_sessionid(ssl_conn, session_ptr); if (result != QCM_VTLS_RESULT_OK) { printf("获取SSL会话失败,错误码: %d\n", result); return -1; } if (*session_ptr == NULL) { printf("未找到有效的SSL会话\n"); return -1; } printf("SSL会话获取成功\n"); return 0; } ``` #### qcm_ssl_clean_all_sessionid - **函数原型** ```c void qcm_ssl_clean_all_sessionid(void) ``` - **功能描述** 清空全部SSL session会话。 - **参数说明** 无 - **返回值说明** 无 ```{note} 1. 此函数清除所有保存的SSL会话信息。 2. 调用后,所有会话恢复功能将不可用,直到建立新的会话。 3. 建议在内存紧张或安全要求高的情况下调用此函数。 ``` - **示例** ```c void cleanup_all_sessions(void) { qcm_ssl_clean_all_sessionid(); printf("所有SSL会话已清除\n"); } ``` ### 辅助API #### qcm_ssl_version - **函数原型** ```c qosa_size_t qcm_ssl_version(char *buffer, qosa_size_t size) ``` - **功能描述** 获取SSL版本相关信息。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *buffer* | 输出 | char * | 存储版本信息相关地址空间;范围为有效缓冲区指针 | | *size* | 输入 | qosa_size_t | 缓冲区大小;需大于0 | - **返回值说明** 返回实际赋值buffer长度大小 ```{note} 1. 确保缓冲区足够大以容纳版本信息。 2. 版本信息通常包括SSL库名称和版本号。 3. 可用于调试和日志记录。 ``` - **示例** ```c void print_ssl_version(void) { char version_buffer[256]; qosa_size_t length; length = qcm_ssl_version(version_buffer, sizeof(version_buffer)); if (length > 0 && length < sizeof(version_buffer)) { version_buffer[length] = '\0'; printf("SSL版本信息: %s\n", version_buffer); } else { printf("获取SSL版本信息失败\n"); } } ``` #### qcm_ssl_data_pending - **函数原型** ```c qosa_bool_t qcm_ssl_data_pending(qcm_ssl_connect_data_t *connssl) ``` - **功能描述** 获取SSL缓存数据信息状态。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入 | *qcm_ssl_connect_data_t ** | SSL连接对象指针;范围为有效连接对象指针 | - **返回值说明** *0*:没有数据 *1*:有数据 ```{note} 1. 在非阻塞读取前调用此函数可以避免不必要的阻塞。 2. 返回值只表示SSL层有缓存数据,不代表socket层有数据。 3. 对于性能敏感的应用程序,合理使用此函数可以提高效率。 ``` - **示例** ```c int check_pending_data(qcm_ssl_connect_data_t *ssl_conn) { if (qcm_ssl_data_pending(ssl_conn)) { printf("SSL缓存中有待处理数据\n"); return 1; } else { printf("SSL缓存中无待处理数据\n"); return 0; } } ``` #### qcm_ssl_check_ciphersuit_is_valid - **函数原型** ```c qosa_bool_t qcm_ssl_check_ciphersuit_is_valid(int cs_id) ``` - **功能描述** 检索对应的ciphersuit算法是否有效。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *cs_id* | 输入 | int | 加密套件ID;范围为有效加密套件ID | - **返回值说明** *0*:无效 *1*:有效 ```{note} 1. 加密套件ID应与SSL库支持的套件匹配。 2. 使用不支持的加密套件可能导致连接失败。 3. 建议在配置加密套件前使用此函数进行验证。 ``` - **示例** ```c int validate_ciphersuite(int cipher_id) { if (qcm_ssl_check_ciphersuit_is_valid(cipher_id)) { printf("加密套件%d有效\n", cipher_id); return 1; } else { printf("加密套件%d无效\n", cipher_id); return 0; } } ``` #### qcm_ssl_set_hostinfo - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_set_hostinfo(qcm_ssl_connect_data_t *connssl, const char *hostname, qosa_uint16_t port); ``` - **功能描述** 如果开启SNI,或者session则必须配置hostinfo连接信息,用以保存host主机名称以及对应的端口号。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *connssl* | 输入/输出 | *qcm_ssl_connect_data_t ** | SSL连接对象指针;范围为有效连接对象指针 | | *hostname* | 输入 | const char * | 配置SNI的主机名称;范围为有效域名字符串 | | *port* | 输入 | qosa_uint16_t | 配置SNI的主机端口号;范围为有效主机端口号 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. SNI(Server Name Indication)用于虚拟主机场景。 2. 主机名应与服务器证书中的主机名匹配。 3. 端口号用于会话恢复时的标识。 ``` - **示例** ```c int set_host_info(qcm_ssl_connect_data_t *ssl_conn, const char *hostname, uint16_t port) { qcm_vtls_result_status_e result; result = qcm_ssl_set_hostinfo(ssl_conn, hostname, port); if (result != QCM_VTLS_RESULT_OK) { printf("设置主机信息失败,错误码: %d\n", result); return -1; } printf("主机信息设置成功: %s:%d\n", hostname, port); return 0; } ``` #### qcm_ssl_set_cacertex_path - **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_set_cacertex_path(int index, char *path) ``` - **功能描述** 设置全局SSL使用的CA证书,配置将失效用户在 *qcm_ssl_config_t* 结构体中 *ca_cert_path* 配置。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *index* | 输入 | int | 证书索引;范围:0~5(SSL_MAX_CA_CERT_EX_CNT-1) | | *path* | 输入 | char * | 证书路径 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 证书路径应为绝对路径。 2. 证书文件格式应为PEM格式。 3. 多个CA证书可以按索引顺序设置。 ``` - **示例** ```c int set_ca_certificate(int index, const char *cert_path) { qcm_vtls_result_status_e result; char *path_copy = strdup(cert_path); if (path_copy == NULL) { printf("内存分配失败\n"); return -1; } result = qcm_ssl_set_cacertex_path(index, path_copy); if (result != QCM_VTLS_RESULT_OK) { printf("设置CA证书路径失败,错误码: %d\n", result); free(path_copy); return -1; } printf("CA证书路径设置成功: 索引%d -> %s\n", index, cert_path); // 注意:path_copy由SSL库管理,不要释放 return 0; } ``` #### qcm_ssl_get_cacertex_path **函数原型** ```c qcm_vtls_result_status_e qcm_ssl_get_cacertex_path(int index, char **path) ``` - **功能描述** 获取全局对应配置的CA证书路径。 - **参数说明** | **参数名** | **输入/输出** | **类型** | **说明** | | --- | --- | --- | --- | | *index* | 输入 | int | 要获取CA证书路径序号;范围:0~5 | | *path* | 输出 | char ** | 对应的CA证书路径地址;范围:有效双重指针 | - **返回值说明** *QCM_VTLS_RESULT_OK*:函数执行成功 其他错误码:函数执行失败 ```{note} 1. 返回的路径字符串由SSL库管理,不应修改或释放。 2. 索引应在有效范围内。 3. 可用于验证配置或调试。 ``` **示例** ```c int get_ca_certificate_path(int index) { qcm_vtls_result_status_e result; char *path = NULL; result = qcm_ssl_get_cacertex_path(index, &path); if (result != QCM_VTLS_RESULT_OK) { printf("获取CA证书路径失败,错误码: %d\n", result); return -1; } if (path != NULL) { printf("CA证书路径[%d]: %s\n", index, path); } else { printf("CA证书路径[%d]: 未设置\n", index); } return 0; } ``` # 最佳实践 ## 线程安全 - **初始化/去初始化**:***qcm_ssl_init()*** 和 ***qcm_ssl_clean_all_sessionid()*** 不是线程安全的,需要外部同步。 - **连接对象**:每个线程应使用独立的SSL连接对象,避免共享。 - **会话管理**:会话缓存操作需要适当的同步机制。 ## 性能提示 - **会话恢复**:启用会话缓存可以显著提高后续连接速度。 - **连接复用**:尽可能复用SSL连接对象。 - **非阻塞IO**:在高并发场景下使用非阻塞模式。 - **缓冲区大小**:根据应用场景调整读写缓冲区大小。 ## 超时配置建议 - **握手超时**:根据网络状况设置合理的SSL握手超时时间(建议30~60秒)。 - **读写超时**:根据数据传输量设置读写超时。 - **心跳机制**:对于长连接,实现应用层心跳保活。 ## 常见问题排查指南 | **问题** | **原因** | **解决方案** | | --- | --- | --- | | SSL连接失败 | 证书验证失败 | 检查CA证书、客户端证书配置 | | 握手超时 | 网络延迟或服务器问题 | 增加超时时间或检查网络连接 | | 内存不足 | SSL对象未释放 | 确保正确调用 ***qcm_ssl_free()*** | | 数据读取失败 | 连接已关闭 | 检查对端状态或实现重连机制 | | 性能低下 | 未启用会话缓存 | 启用会话缓存功能 | ## 结构体定义 ### qcm_ssl_config_t SSL配置结构体定义如下: ```c typedef struct { qcm_ssl_version_type_e ssl_version; qcm_ssl_transport_type_e transport; int *ciphersuites; qcm_ssl_authmode_e auth_mode; qosa_bool_t sni_enable; char *ca_cert_path[SSL_MAX_CA_CERT_CNT]; char *own_cert_path; char *own_key_path; char *own_key_pwd; char *ca_cert_buffer[SSL_MAX_CA_CERT_CNT]; qcm_ssl_client_cert_type_e client_cert_type; char *own_cert_buffer; char *own_key_buff; int ssl_negotiate_timeout; int ignore_invalid_certsign; qosa_uint32_t ignore_certitem; int ignore_multi_certchain_verify; qcm_ssl_dtls_version_type_e dtls_version; int psk_enable; char *psk_identity; char *psk_key; char psk_key_len; qosa_bool_t renegotiation; qosa_ptr socket_fd; qosa_uint8_t ssl_log_debug; qosa_bool_t session_cache_enable; qosa_uint16_t dtls_mtu_size; qosa_bool_t close_time_mode; char *alpn_name; qosa_bool_t mfl_support; qosa_uint8_t mfl_size; vtls_io_read io_read; vtls_io_write io_write; vtls_io_select io_select; } qcm_ssl_config_t; ``` | **参数** | **类型** | **说明** | | --- | --- | --- | | *ssl_version* | *qcm_ssl_version_type_e* | SSL算法版本类型;范围请参考 *qcm_ssl_version_type_e* 枚举定义 | | *transport* | *qcm_ssl_transport_type_e* | SSL传输类型(TLS/DTLS);范围请参考 *qcm_ssl_transport_type_e* 枚举定义 | | *ciphersuites* | int | 配置指定的支持的算法类型;传入支持算法加密套件的整型数组指针,不推荐客户使用,如需要制定加密套件请联系FAE获取demo | | *auth_mode* | *qcm_ssl_authmode_e* | 鉴权类型;范围请参考qcm_ssl_authmode_e枚举定义 | | *sni_enable* | qosa_bool_t | SNI扩展是否启用
QOSA_TRUE打开SNI,QOSA_FALSE关闭SNI | | *ca_cert_path* | qosa_bool_t | 储存CA根证书存放的路径和名称 | | *own_cert_path* | char | 存放客户端证书路径名称 | | *own_key_path* | char | 存放客户端公钥路径名称 | | *own_key_pwd* | char | 客户端公钥密码字符串 | | *ca_cert_buffer* | char | 储存CA根证书内容 | | *client_cert_type* | *qcm_ssl_client_cert_type_e* | 客户端证书类型是buff模式还是文件方式 | | *own_cert_buffer* | char | 客户端证书内容地址指针 | | *own_key_buff* | char | 客户端证书key内容地址指针 | | *ssl_negotiate_timeout* | int | SSL握手超时时间 | | *ignore_invalid_certsign* | int | 忽略证书中对应项目的某一项错误值 | | *ignore_certitem* | qosa_uint32_t | 是否忽略对证书的校验 | | *ignore_multi_certchain_verify* | int | 忽略多级证书链校验 | | *dtls_version* | *qcm_ssl_dtls_version_type_e* | DTLS版本DTLS1.1 DTLS1.2 | | *psk_enable* | int | 是否使能DTLS PSK功能 | | *psk_identity* | char | 对应DTLS PSK identity只能是字符串 | | *psk_key* | char | DTLS PSK字符可以包含字符0x00 | | *psk_key_len* | char | psk key字符长度 | | *renegotiation* | qosa_bool_t | 是否启用TLS会话重协商 | | *socket_fd* | qosa_ptr | 对应socket连接的连接句柄,可以是原始套接字FD,也可以是强转的指针地址。一般都是在如io_read/io_write/io_select中通过强转为用户自定义的句柄类型 | | *ssl_log_debug* | qosa_uint8_t | ssl调试log是否打开 | | *session_cache_enable* | qosa_bool_t | 选择开启或关闭session cache功能 | | *dtls_mtu_size* | qosa_uint16_t | 配置DTLS MTU大小 | | *close_time_mode* | qosa_bool_t | SSL关闭延时时间是否启用 | | *alpn_name* | char | 存储SSL的ALPN信息 | | *mfl_support* | qosa_bool_t | 是否支持MFL扩展 | | *mfl_size* | qosa_uint8_t | 配置MFL扩展大小 | | *io_read* | vtls_io_read | 配置SSL使用的read io接口函数 | | *io_write* | vtls_io_write | 配置SSL使用的write IO接口函数 | | *io_select* | vtls_io_select | 配置SSL使用的select IO函数接口 | ### qcm_ssl_connect_data_t SSL连接数据结构体定义如下: ```c typedef struct { qcm_ssl_connection_state_e state; qcm_ssl_connect_state_e connecting_state; void *backend; qcm_ssl_config_t ssl_config; char *hostname; int port; qosa_time_t cur_connect_time; qosa_timer_t dtls_retransmission_timer; } qcm_ssl_connect_data_t; ``` | **参数** | **类型** | **说明** | | --- | --- | --- | | *state* | *qcm_ssl_connection_state_e* | 对应SSL最上层状态 | | *connecting_state* | *qcm_ssl_connect_state_e* | 对应SSL内部链接状态变化 | | *backend* | void | 对应SSL后端内部结构体存储信息 | | *ssl_config* | *qcm_ssl_config_t* | 当前SSL链接所用的config配置信息 | | *hostname* | char | 主机名 | | *port* | int | 远程端口 | | *cur_connect_time* | *qosa_time_t* | SSL开始握手连接的当前时间 | | *dtls_retransmission_timer* | *qosa_timer_t* | DTLS的重传定时器 | ## 枚举定义 ### qcm_ssl_version_type_e SSL版本类型枚举定义如下: ```c typedef enum { QCM_SSL_VERSION_0 = 0, /* SSL protocol ver. 3.0 */ QCM_SSL_VERSION_1 = 1, /* TLS protocol ver. 1.0 */ QCM_SSL_VERSION_2 = 2, /* TLS protocol ver. 1.1 */ QCM_SSL_VERSION_3 = 3, /* TLS protocol ver. 1.2 */ QCM_SSL_VERSION_ALL = 4, /* NOTE: select ALL */ QCM_SSL_VERSION_5 = 5, /* TLS protocol ver. 1.3 */ } qcm_ssl_version_type_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_VERSION_0* | SSL协议版本3.0 | | *QCM_SSL_VERSION_1* | TLS协议版本1.0 | | *QCM_SSL_VERSION_2* | TLS协议版本1.1 | | *QCM_SSL_VERSION_3* | TLS协议版本1.2 | | *QCM_SSL_VERSION_ALL* | 选择所有版本 | | *QCM_SSL_VERSION_5* | TLS协议版本1.3 | ### qcm_ssl_transport_type_e SSL传输类型枚举定义如下: ```c typedef enum { QCM_SSL_TLS_PROTOCOL = 0, /* SSL使用TLS连接协议,仅适用用TCP */ QCM_SSL_DTLS_PROTOCOL = 1, /* SSL使用DTLS连接协议,仅适用于UDP通讯 */ } qcm_ssl_transport_type_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_TLS_PROTOCOL* | SSL使用TLS连接协议,仅适用用TCP | | *QCM_SSL_DTLS_PROTOCOL* | SSL使用DTLS连接协议,仅适用于UDP通讯 | ### qcm_ssl_authmode_e SSL认证模式枚举定义如下: ```c typedef enum { QCM_SSL_VERIFY_NULL = 0x0000, /* SSL无认证,不校验服务器证书 */ QCM_SSL_VERIFY_SERVER = 0x0001, /* SSL校验服务器证书 */ QCM_SSL_VERIFY_CLIENT_SERVER = 0x0002, /* SSL连接双向认证,检验服务器证书,服务器校验客户端证书 */ } qcm_ssl_authmode_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_VERIFY_NULL* | SSL无认证,不校验服务器证书 | | *QCM_SSL_VERIFY_SERVER* | SSL校验服务器证书 | | *QCM_SSL_VERIFY_CLIENT_SERVER* | SSL连接双向认证,检验服务器证书,服务器校验客户端证书 | ### qcm_ssl_client_cert_type_e 客户端证书类型枚举定义如下: ```c typedef enum { QCM_SSL_CLIENT_CERT_FILE = 0, /* 客户端证书是以文件形式保存提供 */ QCM_SSL_CLIENT_CERT_BUFFER = 1, /* 客户端证书是以buffer缓存形式提供保存 */ } qcm_ssl_client_cert_type_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_CLIENT_CERT_FILE* | 客户端证书是以文件形式保存提供 | | *QCM_SSL_CLIENT_CERT_BUFFER* | 客户端证书是以buffer缓存形式提供保存 | ### qcm_ssl_dtls_version_type_e DTLS版本类型枚举定义如下: ```c typedef enum { QCM_SSL_VER_DTLS10 = 0, /* DTLS protocol ver. 1.0. */ QCM_SSL_VER_DTLS12 = 1, /* DTLS protocal ver. 1.2. */ QCM_SSL_VER_DTLS_ALL = 2, /* 支持DTLS1.0 DTLS1.2服务器自选 */ } qcm_ssl_dtls_version_type_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_VER_DTLS10* | DTLS协议版本1.0 | | *QCM_SSL_VER_DTLS12* | DTLS协议版本1.2 | | *QCM_SSL_VER_DTLS_ALL* | 支持DTLS1.0 DTLS1.2服务器自选 | ### qcm_ssl_connection_state_e SSL连接状态枚举定义如下: ```c typedef enum { QCM_SSL_CONNECTION_NONE, /*!< SSL链接初始状态 */ QCM_SSL_CONNECTION_NEGOTIATING, /*!< 正在协商中 */ QCM_SSL_CONNECTION_COMPLETE /*!< 协商完成 */ } qcm_ssl_connection_state_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_CONNECTION_NONE* | SSL链接初始状态 | | *QCM_SSL_CONNECTION_NEGOTIATING* | 正在协商中 | | *QCM_SSL_CONNECTION_COMPLETE* | 协商完成 | ### qcm_ssl_connect_state_e SSL连接内部状态枚举定义如下: ```c typedef enum { QCM_SSL_CONNECT_1, /*!< 初始阶段SSL初始化配置 */ QCM_SSL_CONNECT_2, /*!< SSL链接鉴权阶段 */ QCM_SSL_CONNECT_2_READING, /*!< SSL异步Socket状态读取数据阶段 */ QCM_SSL_CONNECT_2_WRITING, /*!< SSL异步Socket状态写入数据阶段 */ QCM_SSL_CONNECT_3, /*!< SSL链接已经完成,最后的信息保存 */ QCM_SSL_CONNECT_DONE /*!< SSL链接完成 */ } qcm_ssl_connect_state_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_SSL_CONNECT_1* | 初始阶段SSL初始化配置 | | *QCM_SSL_CONNECT_2* | SSL链接鉴权阶段 | | *QCM_SSL_CONNECT_2_READING* | SSL异步Socket状态读取数据阶段 | | *QCM_SSL_CONNECT_2_WRITING* | SSL异步Socket状态写入数据阶段 | | *QCM_SSL_CONNECT_3* | SSL链接已经完成,最后的信息保存 | | *QCM_SSL_CONNECT_DONE* | SSL链接完成 | ### qcm_vtls_result_status_e VTLS结果状态枚举定义如下: ```c typedef enum { QCM_VTLS_RESULT_OK = 0, QCM_VTLS_FAILED_INIT_ERR = 1 | QCM_ERRCODE_VTLS_BASE, QCM_VTLS_SSL_CONNECT_ERR, QCM_VTLS_SSL_INVALID_PARAM_ERR, QCM_VTLS_PEER_FAILED_VERIFICATION, QCM_VTLS_SSL_READ_WRITE_EAGAIN, QCM_VTLS_SSL_OPERATION_TIMEDOUT, QCM_VTLS_SSL_MEMORY_ERR, QCM_VTLS_SSL_PEER_CLOSE_NOTIFY_ERR, QCM_VTLS_RESULT_MAX } qcm_vtls_result_status_e; ``` | **成员** | **说明** | | --- | --- | | *QCM_VTLS_RESULT_OK* | 操作成功 | | *QCM_VTLS_FAILED_INIT_ERR* | 初始化错误 | | *QCM_VTLS_SSL_CONNECT_ERR* | SSL连接错误 | | *QCM_VTLS_SSL_INVALID_PARAM_ERR* | SSL连接无效参数 | | *QCM_VTLS_PEER_FAILED_VERIFICATION* | 证书校验错误 | | *QCM_VTLS_SSL_READ_WRITE_EAGAIN* | 非阻塞IO函数返回状态,需要等待事件通知到达后再次调用 | | *QCM_VTLS_SSL_OPERATION_TIMEDOUT* | SSL连接操作超时 | | *QCM_VTLS_SSL_MEMORY_ERR* | 内部内存申请失败 | | *QCM_VTLS_SSL_PEER_CLOSE_NOTIFY_ERR* | 通知准备关闭,正常结果码 | | *QCM_VTLS_RESULT_MAX* | 枚举边界 | # 应用逻辑流程图 ```{image} images/image_PAeLbtKR1odVQ8xA0PScnLrBnBh.webp :width: 996px :height: 1821px :align: center ``` # Demo ## 阻塞式SSL连接示例 完整示例代码,请查看https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/ssl/ssl_blocking_demo.c