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代码参考:
cpp
#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 callback
int32_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 verify
if (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 certificate
HITLS_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 certificate
HITLS_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 actual
scenario */
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代码参考:
cpp
#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 callback
int32_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 number
struct 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 check
if (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 certificate
HITLS_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 certificate
HITLS_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 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 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旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!