【密码学实战】国密TLCP协议简介及代码实现示例
TLCP(Transport Layer Cryptography Protocol),中文全称为 传输层密码协议,是中国国家密码管理局(现为国家密码管理局)制定并发布的 国家标准(GB/T 38636-2020) 和 密码行业标准(GM/T 0024-2014),广泛应用于金融、政务、能源等对数据安全要求较高的领域。
核心目标与定位
TLCP 的核心目标与广泛应用的 TLS/SSL 协议类似:在传输层(通常是 TCP 之上)为应用层协议(如 HTTP, FTP, SMTP, IMAP 等)提供端到端的安全通信保障。其主要解决的问题包括:
-
身份认证: 确保通信双方(通常是客户端与服务器)身份的真实性,防止中间人攻击。
-
数据加密: 对传输的应用程序数据进行强加密,防止窃听和泄密。
-
数据完整性: 确保传输的数据在过程中未被篡改。
-
抗重放攻击: 防止攻击者截获并重复发送有效的数据包。
关键特性与技术基础
TLCP 的设计遵循了国际主流传输层安全协议(如 TLS)的基本框架和密码学原理,但其核心密码算法完全采用我国自主研发的 商用密码算法体系(国密算法):
-
国密算法为核心:
-
非对称加密/签名: 主要采用 SM2 椭圆曲线公钥密码算法 用于密钥交换和数字签名,替代 RSA/ECDSA。
-
对称加密: 采用 SM4 分组密码算法 用于加密通信数据,替代 AES/DES/3DES。
-
散列函数: 采用 SM3 密码杂凑算法 用于生成消息摘要、计算消息认证码(MAC)和伪随机函数(PRF),替代 SHA-1/SHA-256/MD5。
-
可选标识密码: 标准也支持 SM9 标识密码算法 用于身份认证和密钥协商。
-
-
双证书体系:
TLCP 的一个显著特点是 推荐使用双证书机制:-
签名证书: 用于身份认证和生成数字签名(使用 SM2-with-SM3)。
-
加密证书: 专门用于密钥交换过程中的加密操作(使用 SM2 加密)。
这种分离提高了密钥使用的安全性和合规性,符合我国密码管理要求,并有助于防范某些类型的攻击(如私钥泄露导致签名和加密同时失效)。
-
-
预定义加密套件:
TLCP 定义了一系列标准的 加密套件(Cipher Suite),明确指定了在握手和通信过程中使用的国密算法组合。例如:-
ECC-SM4-SM3
: 使用 SM2(属于 ECC)进行密钥交换和认证,SM4 加密,SM3 用于 MAC 和 PRF。 -
ECDHE-SM4-SM3
: 使用 SM2 的临时椭圆曲线密钥交换 (ECDHE) 增强前向安全性,其余同上。 -
IBSDH-SM4-SM3
: 使用 SM9 标识密码算法进行密钥交换和认证。
-
-
兼容性与演进:
-
在协议格式和握手流程上,TLCP 很大程度上参考了 TLS 1.1 的框架(如记录层结构、握手消息类型),并吸收了 TLS 1.2/1.3 的一些安全增强思想(如更严格地弃用弱算法、更强调前向安全等)。
-
设计时考虑了与现有网络基础设施的兼容性,可以像 TLS 一样通过不同的端口或应用层协议协商(如 ALPN)来启用
-
TLCP的协议流程(简要)
- 握手协商:客户端与服务端交换
ClientHello
和ServerHello
消息,协商密码套件和随机数 - 身份验证:服务端发送签名证书和加密证书,客户端验证服务端身份。
- 密钥交换:通过SM2算法生成临时会话密钥,确保通信加密。
- 安全通信:双方使用生成的主密钥对数据进行加密和解密,完成安全传输。
开源代码实现示例:
- TLCP Server代码参考:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "securec.h"
#include "bsl_sal.h"
#include "bsl_err.h"
#include "crypt_algid.h"
#include "crypt_eal_init.h"
#include "crypt_eal_rand.h"
#include "crypt_eal_pkey.h"
#include "crypt_eal_codecs.h"
#include "hitls_error.h"
#include "hitls_config.h"
#include "hitls.h"
#include "hitls_cert_init.h"
#include "hitls_cert.h"
#include "hitls_crypt_init.h"
#include "hitls_pki_cert.h"
#include "crypt_errno.h"
#include "bsl_log.h"#define CERTS_PATH "../../../testcode/testdata/tls/certificate/der/sm2_with_userid/"
#define HTTP_BUF_MAXLEN (18 * 1024) /* 18KB */static int32_t HiTLSInit()
{/* Register BSL memory capacity, for reference only */BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_MALLOC, malloc);BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_FREE, free);BSL_ERR_Init();// Registration certificate, crypto callbackint32_t ret = CRYPT_EAL_Init(CRYPT_EAL_INIT_CPU | CRYPT_EAL_INIT_PROVIDER);if (ret != CRYPT_SUCCESS) {printf("CRYPT_EAL_Init: error code is %x\n", ret);return -1;}ret = CRYPT_EAL_ProviderRandInitCtx(NULL, CRYPT_RAND_SHA256, "provider=default", NULL, 0, NULL);if (ret != CRYPT_SUCCESS) {printf("Init rand failed.\n");return -1;}HITLS_CertMethodInit();HITLS_CryptMethodInit();
}int main(int32_t argc, char *argv[])
{int32_t exitValue = -1;int32_t ret = 0;int32_t port = 12345;HITLS_Config *config = NULL;HITLS_Ctx *ctx = NULL;BSL_UIO *uio = NULL;int fd = 0;int infd = 0;HITLS_X509_Cert *rootCA = NULL;HITLS_X509_Cert *subCA = NULL;HITLS_X509_Cert *serverCert = NULL;CRYPT_EAL_PkeyCtx *pkey = NULL;if (HiTLSInit() != 0) {goto EXIT;}fd = socket(AF_INET, SOCK_STREAM, 0);if (fd == -1) {printf("Create socket failed.\n");return -1;}int option = 1;if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) {printf("setsockopt SO_REUSEADDR failed.\n");goto EXIT;}struct sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(port);serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) != 0) {printf("bind failed.\n");goto EXIT;}if (listen(fd, 5) != 0) {printf("listen socket fail\n");goto EXIT;}struct sockaddr_in clientAddr;unsigned int len = sizeof(struct sockaddr_in);infd = accept(fd, (struct sockaddr *)&clientAddr, &len);if (infd < 0) {printf("accept failed.\n");goto EXIT;}config = HITLS_CFG_NewTLCPConfig();if (config == NULL) {printf("HITLS_CFG_NewTLS12Config failed.\n");goto EXIT;}ret = HITLS_CFG_SetClientVerifySupport(config, false); // disable peer verifyif (ret != HITLS_SUCCESS) {printf("Disable peer verify faild.\n");goto EXIT;}/* Load root certificate and intermediate certificate */ret = HITLS_X509_CertParseFile(BSL_FORMAT_PEM, CERTS_PATH "ca.crt", &rootCA);if (ret != HITLS_SUCCESS) {printf("Parse ca failed.\n");goto EXIT;}ret = HITLS_X509_CertParseFile(BSL_FORMAT_PEM, CERTS_PATH "inter.crt", &subCA);if (ret != HITLS_SUCCESS) {printf("Parse subca failed.\n");goto EXIT;}HITLS_CFG_AddCertToStore(config, rootCA, TLS_CERT_STORE_TYPE_DEFAULT, true);HITLS_CFG_AddCertToStore(config, subCA, TLS_CERT_STORE_TYPE_DEFAULT, true);// Load signature certificateHITLS_CERT_X509 *signCert = NULL;HITLS_CERT_X509 *signPkey = NULL;signCert = HITLS_CFG_ParseCert(config, CERTS_PATH "sign.crt",strlen(CERTS_PATH "sign.crt"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (signCert == NULL) {printf("Parse signCert failed.\n");goto EXIT;}signPkey = HITLS_CFG_ParseKey(config, CERTS_PATH "sign.key",strlen(CERTS_PATH "sign.key"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (signPkey == NULL) {printf("Parse signPkey failed.\n");goto EXIT;}HITLS_CFG_SetTlcpCertificate(config, signCert, TLS_PARSE_FORMAT_ASN1, false);HITLS_CFG_SetTlcpPrivateKey(config, signPkey, TLS_PARSE_FORMAT_ASN1, false);// Load encryption certificateHITLS_CERT_X509 *encCert = NULL;HITLS_CERT_X509 *encPkey = NULL;encCert = HITLS_CFG_ParseCert(config, CERTS_PATH "enc.crt",strlen(CERTS_PATH "enc.crt"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (encCert == NULL) {printf("Parse encCert failed.\n");goto EXIT;}encPkey = HITLS_CFG_ParseKey(config, CERTS_PATH "enc.key",strlen(CERTS_PATH "enc.key"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (encPkey == NULL) {printf("Parse encPkey failed.\n");goto EXIT;}HITLS_CFG_SetTlcpCertificate(config, encCert, TLS_PARSE_FORMAT_ASN1, true);HITLS_CFG_SetTlcpPrivateKey(config, encPkey, TLS_PARSE_FORMAT_ASN1, true);/* Create a new openHiTLS ctx */ctx = HITLS_New(config);if (ctx == NULL) {printf("HITLS_New failed.\n");goto EXIT;}/* Users can implement methods as needed */uio = BSL_UIO_New(BSL_UIO_TcpMethod());if (uio == NULL) {printf("BSL_UIO_New failed.\n");goto EXIT;}ret = BSL_UIO_Ctrl(uio, BSL_UIO_SET_FD, (int32_t)sizeof(fd), &infd);if (ret != HITLS_SUCCESS) {BSL_UIO_Free(uio);printf("BSL_UIO_SET_FD failed, fd = %u.\n", fd);goto EXIT;}ret = HITLS_SetUio(ctx, uio);if (ret != HITLS_SUCCESS) {BSL_UIO_Free(uio);printf("HITLS_SetUio failed. ret = 0x%x.\n", ret);goto EXIT;}/* To establish a TLS connection, users need to consider the return value based on the actual scenario */ret = HITLS_Accept(ctx);if (ret != HITLS_SUCCESS) {printf("HITLS_Accept failed, ret = 0x%x.\n", ret);goto EXIT;}/* Sending messages to the other end, users need to consider the return value according to the actual scenario */uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};uint32_t readLen = 0;ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);if (ret != HITLS_SUCCESS) {printf("HITLS_Read failed, ret = 0x%x.\n", ret);goto EXIT;}printf("get from client size:%u :%s\n", readLen, readBuf);/* Read the message from the other end, and the user needs to consider the return value according to the actualscenario */const uint8_t sndBuf[] = "Hi, this is tlcp server\n";uint32_t writeLen = 0;ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf), &writeLen);if (ret != HITLS_SUCCESS) {printf("HITLS_Write error:error code:%d\n", ret);goto EXIT;}exitValue = 0;
EXIT:HITLS_Close(ctx);HITLS_Free(ctx);HITLS_CFG_FreeConfig(config);close(fd);close(infd);HITLS_X509_CertFree(rootCA);HITLS_X509_CertFree(subCA);HITLS_X509_CertFree(serverCert);CRYPT_EAL_PkeyFreeCtx(pkey);BSL_UIO_Free(uio);return exitValue;
}
- TLCP Client代码参考:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "securec.h"
#include "bsl_sal.h"
#include "bsl_err.h"
#include "crypt_eal_init.h"
#include "crypt_algid.h"
#include "crypt_eal_rand.h"
#include "hitls_error.h"
#include "hitls_config.h"
#include "hitls.h"
#include "hitls_cert_init.h"
#include "hitls_cert.h"
#include "hitls_crypt_init.h"
#include "hitls_pki_cert.h"
#include "crypt_errno.h"
#include "bsl_log.h"#define CERTS_PATH "../../../testcode/testdata/tls/certificate/der/sm2_with_userid/"
#define HTTP_BUF_MAXLEN (18 * 1024) /* 18KB */static int32_t HiTLSInit()
{/* Register BSL memory capacity, for reference only */BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_MALLOC, malloc);BSL_SAL_CallBack_Ctrl(BSL_SAL_MEM_FREE, free);BSL_ERR_Init();// Registration certificate, crypto callbackint32_t ret = CRYPT_EAL_Init(CRYPT_EAL_INIT_CPU | CRYPT_EAL_INIT_PROVIDER);if (ret != CRYPT_SUCCESS) {printf("CRYPT_EAL_Init: error code is %x\n", ret);return -1;}ret = CRYPT_EAL_ProviderRandInitCtx(NULL, CRYPT_RAND_SHA256, "provider=default", NULL, 0, NULL);if (ret != CRYPT_SUCCESS) {printf("Init rand failed.\n");return -1;}HITLS_CertMethodInit();HITLS_CryptMethodInit();
}int main(int32_t argc, char *argv[])
{int32_t exitValue = -1;int32_t ret = 0;int32_t port = 12345;HITLS_Config *config = NULL;HITLS_Ctx *ctx = NULL;BSL_UIO *uio = NULL;int fd = 0;HITLS_X509_Cert *rootCA = NULL;HITLS_X509_Cert *subCA = NULL;if (HiTLSInit() != 0) {goto EXIT;}fd = socket(AF_INET, SOCK_STREAM, 0);if (fd == -1) {printf("Create socket failed.\n");goto EXIT;}int option = 1;if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) < 0) {close(fd);printf("setsockopt SO_REUSEADDR failed.\n");goto EXIT;}// Set the protocol and port numberstruct sockaddr_in serverAddr;(void)memset_s(&serverAddr, sizeof(serverAddr), 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(port);serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) != 0) {printf("connect failed.\n");goto EXIT;}config = HITLS_CFG_NewTLCPConfig();if (config == NULL) {printf("HITLS_CFG_NewTLS12Config failed.\n");goto EXIT;}ret = HITLS_CFG_SetCheckKeyUsage(config, false); // disable cert keyusage checkif (ret != HITLS_SUCCESS) {printf("Disable check KeyUsage failed.\n");goto EXIT;}/* Load root certificate and intermediate certificate */ret = HITLS_X509_CertParseFile(BSL_FORMAT_PEM, CERTS_PATH "ca.crt", &rootCA);if (ret != HITLS_SUCCESS) {printf("Parse ca failed.\n");goto EXIT;}ret = HITLS_X509_CertParseFile(BSL_FORMAT_PEM, CERTS_PATH "inter.crt", &subCA);if (ret != HITLS_SUCCESS) {printf("Parse subca failed.\n");goto EXIT;}HITLS_CFG_AddCertToStore(config, rootCA, TLS_CERT_STORE_TYPE_DEFAULT, true);HITLS_CFG_AddCertToStore(config, subCA, TLS_CERT_STORE_TYPE_DEFAULT, true);// Load signature certificateHITLS_CERT_X509 *signCert = NULL;HITLS_CERT_X509 *signPkey = NULL;signCert = HITLS_CFG_ParseCert(config, CERTS_PATH "sign.crt",strlen(CERTS_PATH "sign.crt"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (signCert == NULL) {printf("Parse signCert failed.\n");goto EXIT;}signPkey = HITLS_CFG_ParseKey(config, CERTS_PATH "sign.key",strlen(CERTS_PATH "sign.key"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (signPkey == NULL) {printf("Parse signPkey failed.\n");goto EXIT;}HITLS_CFG_SetTlcpCertificate(config, signCert, TLS_PARSE_FORMAT_ASN1, false);HITLS_CFG_SetTlcpPrivateKey(config, signPkey, TLS_PARSE_FORMAT_ASN1, false);// Load encryption certificateHITLS_CERT_X509 *encCert = NULL;HITLS_CERT_X509 *encPkey = NULL;encCert = HITLS_CFG_ParseCert(config, CERTS_PATH "enc.crt",strlen(CERTS_PATH "enc.crt"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (encCert == NULL) {printf("Parse encCert failed.\n");goto EXIT;}encPkey = HITLS_CFG_ParseKey(config, CERTS_PATH "enc.key",strlen(CERTS_PATH "enc.key"), TLS_PARSE_TYPE_FILE, TLS_PARSE_FORMAT_PEM);if (encPkey == NULL) {printf("Parse encPkey failed.\n");goto EXIT;}HITLS_CFG_SetTlcpCertificate(config, encCert, TLS_PARSE_FORMAT_ASN1, true);HITLS_CFG_SetTlcpPrivateKey(config, encPkey, TLS_PARSE_FORMAT_ASN1, true);/* Create a new openHiTLS ctx */ctx = HITLS_New(config);if (ctx == NULL) {printf("HITLS_New failed.\n");goto EXIT;}uio = BSL_UIO_New(BSL_UIO_TcpMethod());if (uio == NULL) {printf("BSL_UIO_New failed.\n");goto EXIT;}ret = BSL_UIO_Ctrl(uio, BSL_UIO_SET_FD, (int32_t)sizeof(fd), &fd);if (ret != HITLS_SUCCESS) {BSL_UIO_Free(uio);printf("BSL_UIO_SET_FD failed, fd = %u.\n", fd);goto EXIT;}ret = HITLS_SetUio(ctx, uio);if (ret != HITLS_SUCCESS) {BSL_UIO_Free(uio);printf("HITLS_SetUio failed. ret = 0x%x.\n", ret);goto EXIT;}/* To establish a TLS connection, users need to consider the return value based on the actual scenario */ret = HITLS_Connect(ctx);if (ret != HITLS_SUCCESS) {printf("HITLS_Connect failed, ret = 0x%x.\n", ret);goto EXIT;}/* Sending messages to the other end, users need to consider the return value according to the actual scenario */const uint8_t sndBuf[] = "Hi, this is tlcp client\n";uint32_t writeLen = 0;ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf), &writeLen);if (ret != HITLS_SUCCESS) {printf("HITLS_Write error:error code:%d\n", ret);goto EXIT;}/* Read the message from the other end, and the user needs to consider the return value according to the actualscenario */uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};uint32_t readLen = 0;ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);if (ret != HITLS_SUCCESS) {printf("HITLS_Read failed, ret = 0x%x.\n", ret);goto EXIT;}printf("get from server size:%u :%s\n", readLen, readBuf);exitValue = 0;
EXIT:HITLS_Close(ctx);HITLS_Free(ctx);HITLS_CFG_FreeConfig(config);close(fd);HITLS_X509_CertFree(rootCA);HITLS_X509_CertFree(subCA);BSL_UIO_Free(uio);return exitValue;
}
【】免费下载openHiTLS开源代码】
openHiTLS旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
项目地址:https://gitcode.com/openHiTLS/openhitls