【密码学实战】国密TLCP协议简介及代码实现示例

TLCP(Transport Layer Cryptography Protocol) ,中文全称为 传输层密码协议 ,是中国国家密码管理局(现为国家密码管理局)制定并发布的 国家标准(GB/T 38636-2020)密码行业标准(GM/T 0024-2014),广泛应用于金融、政务、能源等对数据安全要求较高的领域。

核心目标与定位

TLCP 的核心目标与广泛应用的 TLS/SSL 协议类似:在传输层(通常是 TCP 之上)为应用层协议(如 HTTP, FTP, SMTP, IMAP 等)提供端到端的安全通信保障。其主要解决的问题包括:

  1. 身份认证: 确保通信双方(通常是客户端与服务器)身份的真实性,防止中间人攻击。

  2. 数据加密: 对传输的应用程序数据进行强加密,防止窃听和泄密。

  3. 数据完整性: 确保传输的数据在过程中未被篡改。

  4. 抗重放攻击: 防止攻击者截获并重复发送有效的数据包。

关键特性与技术基础

TLCP 的设计遵循了国际主流传输层安全协议(如 TLS)的基本框架和密码学原理,但其核心密码算法完全采用我国自主研发的 商用密码算法体系(国密算法)

  1. 国密算法为核心:

    • 非对称加密/签名: 主要采用 SM2 椭圆曲线公钥密码算法 用于密钥交换和数字签名,替代 RSA/ECDSA。

    • 对称加密: 采用 SM4 分组密码算法 用于加密通信数据,替代 AES/DES/3DES。

    • 散列函数: 采用 SM3 密码杂凑算法 用于生成消息摘要、计算消息认证码(MAC)和伪随机函数(PRF),替代 SHA-1/SHA-256/MD5。

    • 可选标识密码: 标准也支持 SM9 标识密码算法 用于身份认证和密钥协商。

  2. 双证书体系:

    TLCP 的一个显著特点是 推荐使用双证书机制

    • 签名证书: 用于身份认证和生成数字签名(使用 SM2-with-SM3)。

    • 加密证书: 专门用于密钥交换过程中的加密操作(使用 SM2 加密)。

      这种分离提高了密钥使用的安全性和合规性,符合我国密码管理要求,并有助于防范某些类型的攻击(如私钥泄露导致签名和加密同时失效)。

  3. 预定义加密套件:

    TLCP 定义了一系列标准的 加密套件(Cipher Suite),明确指定了在握手和通信过程中使用的国密算法组合。例如:

    • ECC-SM4-SM3: 使用 SM2(属于 ECC)进行密钥交换和认证,SM4 加密,SM3 用于 MAC 和 PRF。

    • ECDHE-SM4-SM3: 使用 SM2 的临时椭圆曲线密钥交换 (ECDHE) 增强前向安全性,其余同上。

    • IBSDH-SM4-SM3: 使用 SM9 标识密码算法进行密钥交换和认证。

  4. 兼容性与演进:

    • 在协议格式和握手流程上,TLCP 很大程度上参考了 TLS 1.1 的框架(如记录层结构、握手消息类型),并吸收了 TLS 1.2/1.3 的一些安全增强思想(如更严格地弃用弱算法、更强调前向安全等)。

    • 设计时考虑了与现有网络基础设施的兼容性,可以像 TLS 一样通过不同的端口或应用层协议协商(如 ALPN)来启用

TLCP的协议流程(简要)
  1. 握手协商 :客户端与服务端交换 ClientHelloServerHello消息,协商密码套件和随机数
  2. 身份验证:服务端发送签名证书和加密证书,客户端验证服务端身份。
  3. 密钥交换:通过SM2算法生成临时会话密钥,确保通信加密。
  4. 安全通信:双方使用生成的主密钥对数据进行加密和解密,完成安全传输。

开源代码实现示例:

  • 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开源代码

openHiTLS旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!

项目地址:https://gitcode.com/openHiTLS/openhitls

相关推荐
hrrrrb5 天前
【密码学】6. 消息认证和哈希函数
算法·密码学·哈希算法
hrrrrb5 天前
【密码学】8. 密码协议
密码学
景彡先生6 天前
密码学侧信道攻击(Side-channel Attack):从物理泄露中窃取密钥
密码学
小明的小名叫小明7 天前
区块链技术原理(1) -密码学
区块链·密码学·哈希算法
景彡先生8 天前
基于编码的密码学与Classic McEliece:后量子时代的稳健之选
密码学
白帽程序员猫哥8 天前
漏洞全讲解之中间件与框架漏洞(数字基础设施的“阿喀琉斯之踵“)
网络·安全·web安全·中间件·密码学·渗透
景彡先生9 天前
密码学分组模式详解:从ECB到GCM的进化之路
网络·密码学
做题不NG9 天前
密码学系列文(4)--公钥密码
密码学
景彡先生9 天前
古典密码学:凯撒密码与维吉尼亚密码的原理与实现
密码学