在Linux上用C语言手搓一个国密TLCP服务器:从SM2双证书配置到通信测试 在Linux上用C语言手搓一个国密TLCP服务器从SM2双证书配置到通信测试当我们需要在金融、政务等高安全要求的场景中构建通信系统时传统的TLS协议可能无法满足特定合规要求。这时采用国密标准的TLCP协议就成为了一个可靠的选择。本文将带你从零开始在Linux环境下用C语言实现一个完整的TLCP服务器涵盖双证书配置、openHiTLS库集成到实际通信测试的全流程。1. 环境准备与依赖安装在开始编码前我们需要准备好开发环境。推荐使用Ubuntu 20.04 LTS或CentOS 8作为开发平台这些系统对国密算法的支持较为完善。首先安装基础开发工具链sudo apt update sudo apt install -y build-essential cmake git openssl libssl-dev接下来获取openHiTLS源代码git clone https://gitcode.com/openHiTLS/openhitls.git cd openhitls mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease make -j$(nproc) sudo make install关键依赖说明openHiTLS提供了完整的国密算法实现和TLCP协议栈OpenSSL虽然我们主要使用国密算法但部分基础功能仍依赖OpenSSLCMake用于构建openHiTLS项目提示如果遇到链接错误可能需要设置LD_LIBRARY_PATH环境变量指向openHiTLS的安装目录。2. 国密双证书生成与配置TLCP协议的一个核心特点是采用双证书体系签名证书和加密证书。下面我们使用gmssl工具生成这些证书。首先安装gmsslwget https://github.com/guanzhi/GmSSL/archive/refs/tags/v3.0.0.tar.gz tar xvf v3.0.0.tar.gz cd GmSSL-3.0.0 ./config --prefix/usr/local/gmssl make sudo make install生成CA根证书/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -out ca.key /usr/local/gmssl/bin/gmssl req -new -key ca.key -out ca.csr -subj /CCN/STBeijing/LBeijing/OExample/OUCA/CNRoot CA /usr/local/gmssl/bin/gmssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt生成服务器签名证书/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -out sign.key /usr/local/gmssl/bin/gmssl req -new -key sign.key -out sign.csr -subj /CCN/STBeijing/LBeijing/OExample/OUServer/CNTLCP Server Sign /usr/local/gmssl/bin/gmssl x509 -req -days 365 -in sign.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out sign.crt生成服务器加密证书/usr/local/gmssl/bin/gmssl ecparam -genkey -name sm2p256v1 -out enc.key /usr/local/gmssl/bin/gmssl req -new -key enc.key -out enc.csr -subj /CCN/STBeijing/LBeijing/OExample/OUServer/CNTLCP Server Enc /usr/local/gmssl/bin/gmssl x509 -req -days 365 -in enc.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out enc.crt证书类型对比证书类型用途密钥对扩展密钥用法签名证书身份认证和数字签名SM2digitalSignature加密证书密钥交换加密操作SM2keyEncipherment3. TLCP服务器核心代码实现现在我们来构建TLCP服务器的核心代码。创建一个新文件tlcp_server.c开始实现服务器逻辑。首先包含必要的头文件#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include hitls.h #include hitls_config.h #include hitls_cert.h #include hitls_error.h定义证书路径和缓冲区大小#define CERTS_DIR ./certs/ #define CA_CERT_FILE CERTS_DIR ca.crt #define SIGN_CERT_FILE CERTS_DIR sign.crt #define SIGN_KEY_FILE CERTS_DIR sign.key #define ENC_CERT_FILE CERTS_DIR enc.crt #define ENC_KEY_FILE CERTS_DIR enc.key #define BUFFER_SIZE 4096初始化HiTLS环境的函数static int init_hitls_environment() { // 初始化内存管理回调 if (BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_MALLOC, malloc) ! BSL_SUCCESS || BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_FREE, free) ! BSL_SUCCESS) { fprintf(stderr, Failed to set memory callbacks\n); return -1; } // 初始化基础密码学环境 if (CRYPT_EAL_Init(CRYPT_EAL_INIT_CPU | CRYPT_EAL_INIT_PROVIDER) ! CRYPT_SUCCESS) { fprintf(stderr, CRYPT_EAL_Init failed\n); return -1; } // 初始化随机数生成器 if (CRYPT_EAL_ProviderRandInitCtx(NULL, CRYPT_RAND_SHA256, providerdefault, NULL, 0, NULL) ! CRYPT_SUCCESS) { fprintf(stderr, Failed to initialize random number generator\n); return -1; } // 初始化证书和加密方法 HITLS_CertMethodInit(); HITLS_CryptMethodInit(); return 0; }主函数框架int main() { int server_fd, client_fd; struct sockaddr_in server_addr, client_addr; socklen_t client_len sizeof(client_addr); HITLS_Config *config NULL; HITLS_Ctx *ctx NULL; // 初始化HiTLS环境 if (init_hitls_environment() ! 0) { fprintf(stderr, HiTLS environment initialization failed\n); return EXIT_FAILURE; } // 创建TCP套接字 if ((server_fd socket(AF_INET, SOCK_STREAM, 0)) 0) { perror(socket creation failed); return EXIT_FAILURE; } // 设置套接字选项 int opt 1; if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt))) { perror(setsockopt failed); close(server_fd); return EXIT_FAILURE; } // 绑定套接字 memset(server_addr, 0, sizeof(server_addr)); server_addr.sin_family AF_INET; server_addr.sin_addr.s_addr INADDR_ANY; server_addr.sin_port htons(4433); if (bind(server_fd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) { perror(bind failed); close(server_fd); return EXIT_FAILURE; } // 监听连接 if (listen(server_fd, 5) 0) { perror(listen failed); close(server_fd); return EXIT_FAILURE; } printf(TLCP server listening on port 4433...\n); // 主循环 while (1) { // 接受客户端连接 if ((client_fd accept(server_fd, (struct sockaddr *)client_addr, client_len)) 0) { perror(accept failed); continue; } printf(Client connected: %s:%d\n, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); // 处理TLCP连接 handle_tlcp_connection(client_fd); close(client_fd); } close(server_fd); return EXIT_SUCCESS; }4. TLCP连接处理与安全通信handle_tlcp_connection函数是TLCP协议实现的核心负责完成握手和加密通信。static void handle_tlcp_connection(int client_fd) { HITLS_Config *config NULL; HITLS_Ctx *ctx NULL; BSL_UIO *uio NULL; int ret; // 创建TLCP配置 if ((config HITLS_CFG_NewTLCPConfig()) NULL) { fprintf(stderr, Failed to create TLCP config\n); return; } // 加载CA证书 HITLS_X509_Cert *ca_cert NULL; if (HITLS_X509_CertParseFile(BSL_FORMAT_PEM, CA_CERT_FILE, ca_cert) ! HITLS_SUCCESS) { fprintf(stderr, Failed to parse CA certificate\n); goto cleanup; } HITLS_CFG_AddCertToStore(config, ca_cert, TLS_CERT_STORE_TYPE_DEFAULT, true); // 加载签名证书和私钥 HITLS_CERT_X509 *sign_cert HITLS_CFG_ParseCert(config, SIGN_CERT_FILE, strlen(SIGN_CERT_FILE), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM); if (!sign_cert) { fprintf(stderr, Failed to parse signature certificate\n); goto cleanup; } HITLS_CERT_X509 *sign_key HITLS_CFG_ParseKey(config, SIGN_KEY_FILE, strlen(SIGN_KEY_FILE), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM); if (!sign_key) { fprintf(stderr, Failed to parse signature private key\n); goto cleanup; } // 加载加密证书和私钥 HITLS_CERT_X509 *enc_cert HITLS_CFG_ParseCert(config, ENC_CERT_FILE, strlen(ENC_CERT_FILE), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM); if (!enc_cert) { fprintf(stderr, Failed to parse encryption certificate\n); goto cleanup; } HITLS_CERT_X509 *enc_key HITLS_CFG_ParseKey(config, ENC_KEY_FILE, strlen(ENC_KEY_FILE), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM); if (!enc_key) { fprintf(stderr, Failed to parse encryption private key\n); goto cleanup; } // 配置双证书 if (HITLS_CFG_SetTlcpCertificate(config, sign_cert, TLS_PARSE_FORMAT_ASN1, false) ! HITLS_SUCCESS || HITLS_CFG_SetTlcpPrivateKey(config, sign_key, TLS_PARSE_FORMAT_ASN1, false) ! HITLS_SUCCESS || HITLS_CFG_SetTlcpCertificate(config, enc_cert, TLS_PARSE_FORMAT_ASN1, true) ! HITLS_SUCCESS || HITLS_CFG_SetTlcpPrivateKey(config, enc_key, TLS_PARSE_FORMAT_ASN1, true) ! HITLS_SUCCESS) { fprintf(stderr, Failed to configure dual certificates\n); goto cleanup; } // 创建TLCP上下文 if ((ctx HITLS_New(config)) NULL) { fprintf(stderr, Failed to create TLCP context\n); goto cleanup; } // 设置UIO统一I/O接口 if ((uio BSL_UIO_New(BSL_UIO_TcpMethod())) NULL) { fprintf(stderr, Failed to create UIO\n); goto cleanup; } if (BSL_UIO_Ctrl(uio, BSL_UIO_SET_FD, sizeof(client_fd), client_fd) ! HITLS_SUCCESS) { fprintf(stderr, Failed to set file descriptor\n); goto cleanup; } if (HITLS_SetUio(ctx, uio) ! HITLS_SUCCESS) { fprintf(stderr, Failed to set UIO\n); goto cleanup; } // 执行TLCP握手 if ((ret HITLS_Accept(ctx)) ! HITLS_SUCCESS) { fprintf(stderr, TLCP handshake failed: 0x%x\n, ret); goto cleanup; } printf(TLCP handshake completed successfully\n); // 通信循环 uint8_t buffer[BUFFER_SIZE]; uint32_t len; while (1) { // 读取客户端数据 if ((ret HITLS_Read(ctx, buffer, BUFFER_SIZE - 1, len)) ! HITLS_SUCCESS) { if (ret ! HITLS_ERROR_CONN_CLOSED) { fprintf(stderr, Read error: 0x%x\n, ret); } break; } buffer[len] \0; printf(Received %u bytes: %s\n, len, buffer); // 简单回显 if ((ret HITLS_Write(ctx, buffer, len, len)) ! HITLS_SUCCESS) { fprintf(stderr, Write error: 0x%x\n, ret); break; } } cleanup: if (ctx) { HITLS_Close(ctx); HITLS_Free(ctx); } if (uio) BSL_UIO_Free(uio); if (config) HITLS_CFG_FreeConfig(config); if (ca_cert) HITLS_X509_CertFree(ca_cert); }5. 编译与测试完成代码编写后我们需要编译并测试我们的TLCP服务器。编译命令gcc -o tlcp_server tlcp_server.c -I/usr/local/include/hitls -L/usr/local/lib -lhitls -lssl -lcrypto -lpthread测试步骤启动TLCP服务器./tlcp_server使用openssl s_client测试连接需要支持国密的版本/usr/local/gmssl/bin/gmssl s_client -connect localhost:4433 -cipher ECC-SM4-SM3或者使用我们实现的TLCP客户端进行测试。常见问题排查证书加载失败检查证书路径和权限确保证书文件可读握手失败检查双证书配置是否正确密码套件是否匹配链接错误确认LD_LIBRARY_PATH包含openHiTLS库路径性能优化建议使用连接池管理TLCP上下文避免频繁创建销毁启用会话复用减少握手开销考虑使用硬件加速SM2/SM4运算