TLS特性介绍

协议功能介绍

openHiTLS基于传输层安全协议标准提供了安全协议链路创建、配置、管理等功能,主要功能接口在协议模块中提供。openHiTLS支持多种协议版本以及协议特性功能,包括基础协议握手、密钥更新、应用层协议协商、服务器名称指示等能力。在TLS1.3中,openHiTLS还支持混合密钥交换(Hybrid Key Exchange),为未来量子计算威胁提供防护。

openHiTLS目前支持协议版本如下:

  • TLS1.2:支持安全重协商、应用层协议协商、服务器名称指示、会话恢复。
  • TLS1.3:密钥更新、应用层协议协商、服务器名称指示、会话恢复。
  • DTLS1.2:支持安全重协商、应用层协议协商、服务器名称指示、会话恢复。
  • TLCP:支持安全重协商、会话恢复。

(D)TLS1.2规格说明

配置项 规格说明
TLS版本 TLS12(0x0303u)
DTLS12(0xfefdu)
算法套 TLS_RSA_WITH_AES_128_CBC_SHA(0x002F)
TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032)
TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033)
TLS_DH_anon_WITH_AES_128_CBC_SHA(0x0034)
TLS_RSA_WITH_AES_256_CBC_SHA(0x0035)
TLS_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039)
TLS_DH_anon_WITH_AES_256_CBC_SHA(0x003A)
TLS_RSA_WITH_AES_128_CBC_SHA256(0x003C)
TLS_RSA_WITH_AES_256_CBC_SHA256(0x003D)
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040)
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067)
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B)
TLS_DH_anon_WITH_AES_128_CBC_SHA256(0x006C)
TLS_DH_anon_WITH_AES_256_CBC_SHA256(0x006D)
TLS_PSK_WITH_AES_128_CBC_SHA(0x008C)
TLS_PSK_WITH_AES_256_CBC_SHA(0x008D)
TLS_DHE_PSK_WITH_AES_128_CBC_SHA(0x0090)
TLS_DHE_PSK_WITH_AES_256_CBC_SHA(0x0091)
TLS_RSA_PSK_WITH_AES_128_CBC_SHA(0x0094)
TLS_RSA_PSK_WITH_AES_256_CBC_SHA(0x0095)
TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C)
TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D)
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(0x009E)
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F)
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2)
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3)
TLS_DH_anon_WITH_AES_128_GCM_SHA256(0x00A6)
TLS_DH_anon_WITH_AES_256_GCM_SHA384(0x00A7)
TLS_PSK_WITH_AES_128_GCM_SHA256(0x00A8)
TLS_PSK_WITH_AES_256_GCM_SHA384(0x00A9)
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256(0x00AA)
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384(0x00AB)
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256(0x00AC)
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384(0x00AD)
TLS_PSK_WITH_AES_128_CBC_SHA256(0x00AE)
TLS_PSK_WITH_AES_256_CBC_SHA384(0x00AF)
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256(0x00B2)
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384(0x00B3)
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256(0x00B6)
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384(0x00B7)
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009)
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014)
TLS_ECDH_anon_WITH_AES_128_CBC_SHA(0xC018)
TLS_ECDH_anon_WITH_AES_256_CBC_SHA(0xC019)
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023)
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(0xC024)
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027)
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(0xC028)
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B)
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F)
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030)
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(0xC035)
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(0xC036)
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256(0xC037)
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384(0xC038)
TLS_RSA_WITH_AES_128_CCM(0xC09C)
TLS_RSA_WITH_AES_256_CCM(0xC09D)
TLS_DHE_RSA_WITH_AES_128_CCM(0xC09E)
TLS_DHE_RSA_WITH_AES_256_CCM(0xC09F)
TLS_RSA_WITH_AES_128_CCM_8(0xC0A0)
TLS_RSA_WITH_AES_256_CCM_8(0xC0A1)
TLS_PSK_WITH_AES_256_CCM(0xC0A5)
TLS_DHE_PSK_WITH_AES_128_CCM(0xC0A6)
TLS_DHE_PSK_WITH_AES_256_CCM(0xC0A7)
TLS_ECDHE_ECDSA_WITH_AES_128_CCM(0xC0AC)
TLS_ECDHE_ECDSA_WITH_AES_256_CCM(0xC0AD)
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA8)
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(0xCCA9)
TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(0xCCAA)
TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAB)
TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAC)
TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAD)
TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(0xCCAE)
TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256(0xD001)
TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384(0xD002)
TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256(0xD005)
EC点格式 uncompressed(0)
椭圆曲线 secp256r1(23)
secp384r1(24)
secp521r1(25)
brainpoolP256r1(26)
brainpoolP384r1(27)
brainpoolP512r1(28)
x25519(29)
签名哈希算法 dsa_sha256(0x0402)
dsa_sha384(0x0502)
dsa_sha512(0x0602)
rsa_pkcs1_sha256(0x0401)
rsa_pkcs1_sha384(0x0501)
rsa_pkcs1_sha512(0x0601)
ecdsa_secp256r1_sha256(0x0403)
ecdsa_secp384r1_sha384(0x0503)
ecdsa_secp521r1_sha512(0x0603)
rsa_pss_rsae_sha256(0x0804)
rsa_pss_rsae_sha384(0x0805)
rsa_pss_rsae_sha512(0x0806)
rsa_pss_pss_sha256(0x0809)
rsa_pss_pss_sha384(0x080a)
rsa_pss_pss_sha512(0x080b)
ed25519(0x0807)
是否开启双端校验 HITLS_CFG_SetClientVerifySupport(默认关闭)
是否允许客户端证书为空 HITLS_CFG_SetNoClientCertSupport(默认开启)
是否不校验对端证书 HITLS_CFG_SetVerifyNoneSupport(默认关闭)
是否支持重协商 HITLS_CFG_SetRenegotiationSupport(默认关闭)
是否只校验一次客户端证书 HITLS_CFG_SetClientOnceVerifySupport(默认关闭)
是否开启单航程发送握手报文 HITLS_CFG_SetFlightTransmitSwitch(默认关闭)
是否开启安静断链模式 HITLS_CFG_SetQuietShutdown(默认关闭)
是否支持扩展主密钥 HITLS_CFG_SetExtendedMasterSecretSupport(默认开启)
是否支持sessionTicket HITLS_CFG_SetSessionTicketSupport(默认开启)
是否校验证书keyUsage HITLS_CFG_SetCheckKeyUsage(默认开启)
是否支持自动生成DH参数 HITLS_CFG_SetDhAutoSupport(默认开启)

TLS1.3规格说明

配置项 规格说明
TLS版本 TLS13(0x0304u)
算法套 TLS_AES_128_GCM_SHA256(0x1301)
TLS_AES_256_GCM_SHA384(0x1302)
TLS_CHACHA20_POLY1305_SHA256(0x1303)
TLS_AES_128_CCM_SHA256(0x1304)
TLS_AES_128_CCM_8_SHA256(0x1305)
EC点格式 uncompressed(0)
支持的Group(密钥协商组) 传统椭圆曲线:
secp256r1(23)
secp384r1(24)
secp521r1(25)
x25519(29)
有限域DH组:
ffdhe2048(256)
ffdhe3072(257)
ffdhe4096(258)
ffdhe6144(259)
ffdhe8192(260)
混合密钥交换(Hybrid Key Exchange):
X25519MLKEM768(4588)
SecP256r1MLKEM768(4587)
SecP384r1MLKEM1024(4589)
签名哈希算法 rsa_pkcs1_sha256(0x0401)
rsa_pkcs1_sha384(0x0501)
rsa_pkcs1_sha512(0x0601)
ecdsa_secp256r1_sha256(0x0403)
ecdsa_secp384r1_sha384(0x0503)
ecdsa_secp521r1_sha512(0x0603)
rsa_pss_rsae_sha256(0x0804)
rsa_pss_rsae_sha384(0x0805)
rsa_pss_rsae_sha512(0x0806)
rsa_pss_pss_sha256(0x0809)
rsa_pss_pss_sha384(0x080a)
rsa_pss_pss_sha512(0x080b)
ed25519(0x0807)
是否开启双端校验 HITLS_CFG_SetClientVerifySupport(默认关闭)
是否允许客户端证书为空 HITLS_CFG_SetNoClientCertSupport(默认开启)
是否不校验对端证书 HITLS_CFG_SetVerifyNoneSupport(默认关闭)
是否只校验一次客户端证书 HITLS_CFG_SetClientOnceVerifySupport(默认关闭)
是否开启握手后认证 HITLS_CFG_SetPostHandshakeAuthSupport(默认关闭)
是否开启单航程发送握手报文 HITLS_CFG_SetFlightTransmitSwitch(默认关闭)
是否开启安静断链模式 HITLS_CFG_SetQuietShutdown(默认关闭)
是否支持扩展主密钥 HITLS_CFG_SetExtendedMasterSecretSupport(默认开启)
是否支持sessionTicket HITLS_CFG_SetSessionTicketSupport(默认开启)
是否校验证书keyUsage HITLS_CFG_SetCheckKeyUsage(默认开启)
是否支持自动生成DH参数 HITLS_CFG_SetDhAutoSupport(默认开启)

后量子密码学支持

openHiTLS在TLS1.3中支持混合密钥交换(Hybrid Key Exchange),用于应对未来量子计算机对传统密码学的威胁。混合密钥交换方案结合了传统的ECDH密钥交换和基于格的ML-KEM算法,提供了"防御纵深"的安全保证。

支持的混合密钥交换算法:

算法名称 组成 安全强度 IANA代码点 适用场景
X25519MLKEM768 X25519 + ML-KEM-768 192位 4588 高性能场景,推荐使用
SecP256r1MLKEM768 ECDH(NIST P-256) + ML-KEM-768 192位 4587 需要NIST曲线兼容的场景
SecP384r1MLKEM1024 ECDH(NIST P-384) + ML-KEM-1024 256位 4589 高安全要求场景

使用说明:

  • 混合密钥交换算法仅在TLS 1.3中可用
  • 默认配置中X25519MLKEM768具有最高优先级
  • 需要在编译时启用HITLS_TLS_FEATURE_KEM特性
  • 客户端和服务端都需要支持相同的混合密钥交换算法才能协商成功

API配置示例:

// 设置支持的group,包括混合密钥交换
uint16_t groups[] = {
    HITLS_HYBRID_X25519_MLKEM768,           // 混合密钥交换
    HITLS_EC_GROUP_CURVE25519,              // 传统X25519
    HITLS_EC_GROUP_SECP256R1,               // 传统NIST P-256
};
HITLS_CFG_SetGroups(config, groups, sizeof(groups) / sizeof(groups[0]));

注意: 后量子密码学算法的密钥和密文尺寸较大(例如X25519MLKEM768的公钥长度为1216字节),可能会增加握手消息的大小。

TLCP规格说明

配置项 规格说明
TLCP版本 TLCP11(0x0101u)
算法套 ECDHE_SM4_CBC_SM3(0xE011)
ECC_SM4_CBC_SM3(0xE013)
EC点格式 HITLS_POINT_FORMAT_UNCOMPRESSED(0)
椭圆曲线 curveSM2(41)
签名哈希算法 sm2sig_sm3(0x0708)
是否开启双端校验 HITLS_CFG_SetClientVerifySupport(默认关闭)
是否允许客户端证书为空 HITLS_CFG_SetNoClientCertSupport(默认开启)
是否不校验对端证书 HITLS_CFG_SetVerifyNoneSupport(默认关闭)
是否只校验一次客户端证书 HITLS_CFG_SetClientOnceVerifySupport(默认关闭)
是否开启单航程发送握手报文 HITLS_CFG_SetFlightTransmitSwitch(默认关闭)
是否开启安静断链模式 HITLS_CFG_SetQuietShutdown(默认关闭)
是否校验证书keyUsage HITLS_CFG_SetCheckKeyUsage(默认开启)

扩展能力

扩展类型名称 DTLS1.2 TLS1.2 TLS1.3 TLCP
server_name
supported_groups
ec_point_formats
signature_algorithms
application_layer_protocol_negotiation
extended_master_secret
session_ticket
encrypt_then_mac
renegotiation_info
early_data
supported_versions
cookie
pre_shared_key
psk_key_exchange_modes
certificate_authorities
oid_filters
post_handshake_auth
signature_algorithms_cert
key_share

框架

image

上下文介绍

openHiTLS安全传输的上下文分为两层:HITLS_Config和HITLS_Ctx。其中HITLS_Config是openHiTLS的配置上下文,一般一个进程中的一类业务(如客户端、服务端)有一个配置上下文,HITLS_Ctx是openHiTLS的链路上下文,每条连接一个上下文,配置上下文和链路上下文是多对一的关系。openHiTLS中的每个链路上下文都拷贝了一个配置上下文的副本。

非阻塞IO能力

本模块不提供创建fd(file description)的能力,fd由用户创建后,配置到openHiTLS内,openHiTLS读写fd后,用户再关闭fd。openHiTLS提供非阻塞IO的支持,不管是在握手阶段,还是在读写阶段,都支持非阻塞IO的操作能力。 在调用HITLS_Read、HITLS_Write时,如果返回HITLS_REC_NORMAL_RECV_BUF_EMPTY或HITLS_REC_NORMAL_IO_BUSY,表示openHiTLS需要再次读写。在实际业务中,往往通过epoll/select驱动,以实现非阻塞IO的功能,非阻塞IO的示例代码如下:

// 客户端握手
do {
    ret = HITLS_Connect(ctx);
} while (ret == HITLS_REC_NORMAL_RECV_BUF_EMPTY || ret == HITLS_REC_NORMAL_IO_BUSY);
// 服务端握手
do {
    ret = HITLS_Accept(ctx);
} while (ret == HITLS_REC_NORMAL_RECV_BUF_EMPTY || ret == HITLS_REC_NORMAL_IO_BUSY);

注意: do while仅供参考,业务逻辑实现可能有所不同。

约束说明

  1. openHiTLS支持基于证书认证的客户端和服务端。
  2. 为方便用户快速使用,提供了默认配置功能。大部分情况下,用户只需要在默认配置的基础上,做很小的额外配置,即可让openHiTLS开始工作。 同时,openHiTLS提供了丰富的配置接口,产品可以根据API手册,根据自己的需求配置openHiTLS的选项。

对外依赖

openHiTLS的算法和证书与协议层是解耦的,目前提供了自实现的回调注册能力,注册相关函数如下:

/**
 * @brief   注册默认证书回调函数
 */
int32_t HITLS_CertMethodInit(void);

/**
 * @brief   注册默认算法回调函数
 */
void HITLS_CryptMethodInit(void);

/**
 * @brief   注册内存管理回调函数
 */
int32_t BSL_SAL_CallBack_Ctrl(BSL_SAL_CB_FUNC_TYPE funcType, void *funcCb);

/**
 * @brief   初始化全局随机数
 */
int32_t CRYPT_EAL_RandInit(CRYPT_RAND_AlgId id, CRYPT_RandSeedMethod *seedMeth, void *seedCtx,
    const uint8_t *pers, uint32_t persLen);

安全通信应用时序交互介绍

image

TLS客户端示例

客户端类型

基于证书认证的客户端

基于证书认证的客户端需要配置信任证书池和设备证书,信任证书池用来标识哪些证书颁发机构被客户端信任。

加载信任证书

  • 使用信任证书池:信任证书池用来标识哪些证书颁发机构被客户端信任。用户需要在进行连接前配置信任证书池,用户配置的信任证书池将会被加载到证书管理引擎中。信任证书池分为两类:
  1. 用于校验对端证书链 对于需要校验服务端身份的算法套,服务端会将证书及证书链通过握手消息发送给TLS客户端。若服务端发送的证书及证书链非客户端信任的颁发机构所颁发,客户端将发送致命告警并终止握手。若用户未配置信任证书池,也会导致证书链校验失败,最终造成TLS握手失败。

    对于配置上下文用户可通过以下接口设置用于校验对端证书的信任证书池:

/**
 * @brief   设置TLS配置使用的verify store,用于证书校验
 */
int32_t HITLS_CFG_SetVerifyStore(HITLS_Config *config, HITLS_CERT_Store *store, bool isClone);

对于链路上下文用户则可以调用HITLS_SetVerifyStore进行设置。

注意: 在调用HITLS_CFG_NewXXXConfig时会生成一个默认证书池CertStore,若用户未设置VerifyStore时,默认使用CertStore进行证书链校验。

  1. 用于产生本端证书链 服务端在进行握手时,需要发送本端证书给对端进行校验,若用户未配置本端设备证书对应的证书链,则服务端会从信任证书池中寻找证书链发送给对端。服务端在已经发送证书链的情况下,可以请求TLS客户端证书来校验客户端身份,该场景称为双向认证,TLS客户端会将本端证书及证书链通过握手消息发送给服务端。若用户配置的信任证书池中没有本端设备证书对应的证书链,或者用户未配置信任证书池,客户端会发送空的证书消息,能否继续握手取决于服务端的行为。

    用户可通过以下接口设置产生本端证书链的信任证书池:

/**
 * @brief   设置TLS配置使用的chain store,用于构造证书链
 */
int32_t HITLS_CFG_SetChainStore(HITLS_Config *config, HITLS_CERT_Store *store, bool isClone);
  • 使用设备证书对应证书链:服务端或客户端(双向认证场景)需要给对端发送设备证书以及证书链,除了使用信任证书池之外,还提供根据设备证书添加相应证书链能力。给对端发送证书链时优先级以设备证书相应证书链为先。用户可通过以下接口设置用于添加相应证书链能力:
/**
 * @brief   添加证书到当前config正在使用的证书链中
 */
int32_t HITLS_CFG_AddChainCert(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone);
  • 向信任证书池添加证书:在设置完信任证书池后可以向相应的信任证书池添加信任证书,用户可通过以下接口设置:
/**
 * @brief   向指定信任证书池添加证书
 */
int32_t HITLS_CFG_AddCertToStore(HITLS_Config *config, char *certPath, HITLS_CERT_StoreType storeType);

**说明:**该接口可以为默认证书池、校验证书池和证书链池添加证书,证书以相对路径传入。

配置客户端证书

客户端证书是客户端身份认证的凭证,在双向认证场景下,TLS客户端会将本端证书及证书链通过握手消息发送给服务端,若客户端没有可用的证书,或者用户未配置证书,TLS客户端会发送空的证书消息,能否继续握手取决于服务端的行为。

对于配置上下文用户可通过以下接口配置客户端证书:

/**
 * @brief   添加设备证书,每种类型的证书只能添加一本
 */
int32_t HITLS_CFG_SetCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone);
/**
 * @brief   从文件中加载设备证书
 */
int32_t HITLS_CFG_LoadCertFile(HITLS_Config *config, const uint8_t *file, HITLS_ParseFormat format);
/**
 * @brief   从buffer中读取设备证书
 */
int32_t HITLS_CFG_LoadCertBuffer(HITLS_Config *config, const uint8_t *buf, uint32_t bufLen, HITLS_ParseFormat format);
/**
 * @brief   添加国密设备证书,每种类型的证书只能添加一本
 */
int32_t HITLS_CFG_SetTlcpCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone, bool isTlcpEncCert);

除了配置证书,用户还需要配置证书对应的私钥,只配置证书而不配置对应的私钥同样会导致握手失败。用户可通过以下接口对配置上下文配置证书对应的私钥:

/**
 * @brief   添加设备证书私钥,每种类型的证书只能添加一个私钥
 */
int32_t HITLS_CFG_SetPrivateKey(HITLS_Config *config, HITLS_CERT_Key *privateKey, bool isClone);
/**
 * @brief   从文件中加载设备证书私钥
 */
int32_t HITLS_CFG_LoadKeyFile(HITLS_Config *config, const uint8_t *file, HITLS_ParseFormat format);
/**
 * @brief   从buffer中读取设备证书私钥
 */
int32_t HITLS_CFG_LoadKeyBuffer(HITLS_Config *config, const uint8_t *buf, uint32_t bufLen, HITLS_ParseFormat format);
/**
 * @brief   添加国密设备证书,每种类型的证书只能添加一本
 */
int32_t HITLS_CFG_SetTlcpCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone, bool isTlcpEncCert);

用户可以通过以下接口卸载所有的证书和私钥:

/**
 * @brief   释放加载的所有证书及私钥
 */
int32_t HITLS_CFG_RemoveCertAndKey(HITLS_Config *config);

对于已生成的链路上下文则可以通过以下接口卸载证书和私钥:

/**
 * @brief   释放加载的所有证书及私钥
 */
int32_t HITLS_RemoveCertAndKey(HITLS_Ctx *ctx);

注意:每一类证书及对应的私钥只允许配置一次,重复配置会被覆盖,不同类证书不受影响。比如先后配置两本RSA证书,只有最后配置的RSA证书会生效,但是可以先配置RSA证书,再配置ECDSA证书,两本证书都会生效。

基于PSK认证的客户端

基于PSK认证的基本建链流程如下:

image

  1. 使用PSK密钥协商,客户端在ClientHello消息中提供PSK算法套选择,并由服务端选择是否使用PSK算法套;
  2. 在选定具体的PSK类算法套后,服务端会在ServerKeyExchange消息中包含一个身份提示信息identity_hint,用于提示客户端使用哪一个psk identity_hint;
  3. 客户端在收到包含身份提示信息的ServerKeyExchange消息后,通过回调的形式向TLS使用者索取PSK密钥及identity身份信息;
  4. 随后客户端在ClientKeyExchange消息中包含identity身份信息,用于指示服务端使用哪一个PSK;
  5. 服务端在收到包含identity身份信息后,通过回调的形式向上层索取PSK密钥;
  6. 随后两端基于获取到的PSK生成预主密钥,并在生成后清除PSK,完成密钥协商流程。

因此,基于PSK认证的客户端需要先设置预共享密钥获取回调,回调形式如下:

/**
 * @brief   客户端获取psk原型
 */
typedef uint32_t (*HITLS_PskClientCb)(HITLS_Ctx *ctx, const uint8_t *hint, uint8_t *identity, uint32_t maxIdentityLen, uint8_t *psk, uint32_t maxPskLen);
/**
 * @brief   设置客户端PSK回调,用于在PSK协商时通过该回调获取identity及psk
 */
int32_t HITLS_CFG_SetPskClientCallback(HITLS_Config *config, HITLS_PskClientCb callback);

示例代码

基于证书认证的客户端

参考client.c

基于PSK认证的客户端

基于PSK认证的客户端代码大部分跟基于证书客户端的相同,只是在配置HITLS_Config有所不同。

...
uint32_t ExampleClientCb(HITLS_Ctx *ctx, const uint8_t *hint, uint8_t *identity, uint32_t maxIdentityLen, uint8_t *psk,
    uint32_t maxPskLen)
{
    (void)ctx;
    (void)hint;
    int32_t ret;
    const char pskTrans[] = "psk data";
    uint32_t pskTransUsedLen = sizeof(pskTransUsedLen);
    if (strlen("hello") + 1 > maxIdentityLen) {
        return 0;
    }
    memcpy(identity, "hello", strlen("hello") + 1);

    if (pskTransUsedLen > maxPskLen) {
        return 0;
    }
    memcpy(psk, pskTrans, pskTransUsedLen);

    return pskTransUsedLen;
}


int main(int32_t argc, char *argv[])
{
    ...
    config = HITLS_CFG_NewTLS12Config();
    if (config == NULL) {
        printf("HITLS_CFG_NewTLS12Config failed.\n");
        return -1;
    }
	uint16_t cipherSuite = HITLS_PSK_WITH_AES_128_GCM_SHA256;
	// 配置算法套
    if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
        printf("HITLS_CFG_SetCipherSuites err\n");
        return -1;
    }
	// 配置psk回调
	if (HITLS_CFG_SetPskClientCallback(config, (HITLS_PskClientCb)ExampleClientCb) != HITLS_SUCCESS) {
        printf("HITLS_CFG_SetPskClientCallback err\n");
        return -1;
    }

    /* 新建openHiTLS上下文 */
    ctx = HITLS_New(config);
    if (ctx == NULL) {
        printf("HITLS_New failed.\n");
        goto EXIT;
    }

    ...
}

TLCP客户端

其他步骤与基于证书认证的客户端一致。

config = HITLS_CFG_NewTLCPConfig();
if (config == NULL) {
	printf("HITLS_CFG_NewTLCPConfig failed.\n");
	return -1;
}
uint16_t cipherSuite = HITLS_ECC_SM4_CBC_SM3;
// 配置算法套
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
	printf("HITLS_CFG_SetCipherSuites err\n");
	return -1;
}

/* 加载证书:需要用户实现 */
HITLS_CFG_AddCertToStore(config, "rootCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
HITLS_CFG_AddCertToStore(config, "intCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
// 双端认证场景下,需要从文件中加载签名证书和私钥, 由用户实现
HITLS_CERT_X509 *signCert = LoadCertFromFile("ClientSignCert.pem");
HITLS_CERT_X509 *signKey =  LoadKeyFromFile("ClientSignKey.pem");
// 从文件中加载加密证书和私钥
HITLS_CERT_X509 *encCert = LoadCertFromFile("ClientEncCert.pem");
HITLS_CERT_X509 *encKey = LoadKeyFromFile("ClientEncKey.pem");
//设置添加国密签名证书和私钥
HITLS_CFG_SetTlcpCertificate(config, signCert, false, false);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, false);
//设置添加国密加密证书和私钥
HITLS_CFG_SetTlcpCertificate(config, signCert, false, true);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, true);
...

TLS服务端示例

服务端类型

基于证书认证的服务端

基于证书认证的TLS服务端需要配置信任证书池和设备证书,信任证书池用来标识哪些证书颁发机构被客户端信任,设备证书是服务端身份认证的凭证。服务端可以通过双向认证配置项来决定是否校验客户端身份。

双向认证服务端配置

服务端在已经发送证书链的情况下,可以请求TLS客户端证书来校验客户端身份,该场景称为“双向认证”。 openHiTLS提供以下两个配置项:

  1. 双向认证开关 默认关闭,即服务端默认不校验客户端身份。用户可通过HITLS_CFG_SetClientVerifySupport接口控制开关。
/**
 * @brief   设置是否校验客户端证书
            客户端:此设置无影响
            服务端:将会发送certificate request
 */
int32_t HITLS_CFG_SetClientVerifySupport(HITLS_Config *config, bool support);
  1. 是否接受无客户端证书 此配置仅在双向认证打开的场景下生效,默认情况下,处于启用状态,这意味着如果TLS服务器收到空证书,则验证通过。 用户可通过HITLS_CFG_SetNoClientCertSupport接口控制开关。
/**
 * @brief   设置是否支持没有客户端证书,仅在开启校验客户端证书的场景下生效
            客户端:此设置无影响
            服务端:收到客户端空证书时,证书校验是否通过。默认校验通过
 */
int32_t HITLS_CFG_SetNoClientCertSupport(HITLS_Config *config, bool support);

加载信任证书池

参考[加载信任证书](#### 加载信任证书)。

配置服务端证书

对于需要校验服务端身份的算法套,用户需要配置服务端证书、证书链及对应的私钥。参考配置客户端证书。

基于PSK认证的服务端

基于PSK认证的服务端获取预共享密钥回调略有不同,如下所示:

/**
 * @brief   服务端psk协商回调
*/
typedef int32_t (*HITLS_PskFindSessionCb)(HITLS_Ctx *ctx, const uint8_t *identity, uint32_t identityLen,
    HITLS_Session **session);
/**
 * @brief   设置服务端PSK回调,用于在PSK协商时通过该回调获取psk
 */
int32_t HITLS_CFG_SetPskServerCallback(HITLS_Config *config, HITLS_PskServerCb callback);

剩余流程参考客户端。

示例代码

基于证书认证的服务端

参考server.c

基于PSK认证的服务端

基于PSK认证的服务端代码大部分跟基于证书服务端的相同,只是在配置HITLS_Config有所不同。

...

uint32_t ExampleServerCb(HITLS_Ctx *ctx, const uint8_t *identity, uint8_t *psk, uint32_t maxPskLen)
{
    (void)ctx;
    if (identity == NULL || strcmp((const char *)identity, "hello") != 0) {
        return 0;
    }
    const char pskTrans[] = "psk data";
    uint32_t pskTransUsedLen = sizeof(pskTransUsedLen);
    if (pskTransUsedLen > maxPskLen) {
        return 0;
    }
    memcpy(psk, pskTrans, pskTransUsedLen);
    return pskTransUsedLen;
}

int main(int32_t argc, char *argv[])
{
    ...
    config = HITLS_CFG_NewTLS12Config();
    if (config == NULL) {
        printf("HITLS_CFG_NewTLS12Config failed.\n");
        return -1;
    }
	uint16_t cipherSuite = HITLS_PSK_WITH_AES_128_GCM_SHA256;
	// 配置算法套
    if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
        printf("HITLS_CFG_SetCipherSuites err\n");
        return -1;
    }
	// 配置psk回调
	if (HITLS_CFG_SetPskServerCallback(tlsConfig, (HITLS_PskServerCb)ExampleServerCb) != HITLS_SUCCESS) {
        printf("HITLS_CFG_SetPskClientCallback err\n");
        return -1;
    }

    /* 新建openHiTLS上下文 */
    ctx = HITLS_New(config);
    if (ctx == NULL) {
        printf("HITLS_New failed.\n");
        goto EXIT;
    }

    ...
}

TLCP服务端

其他步骤与基于证书认证的服务端一致。

...
config = HITLS_CFG_NewTLCPConfig();
if (cfg == NULL) {
	printf("HITLS_CFG_NewTLCPConfig failed.\n");
	return -1;
}

uint16_t cipherSuite = HITLS_ECC_SM4_CBC_SM3;
// 配置算法套
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
	printf("HITLS_CFG_SetCipherSuites err\n");
	return -1;
}

if (HITLS_CFG_SetClientVerifySupport(config, true) != HITLS_SUCCESS) {
	printf("HITLS_CFG_SetClientVerifySupport err\n");
	return -1;
}

/* 加载证书:需要用户实现 */
HITLS_CFG_AddCertToStore(config, "rootCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
HITLS_CFG_AddCertToStore(config, "intCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
// 从文件中加载签名证书和私钥, 需要用户实现
HITLS_CERT_X509 *signCert = LoadCertFromFile("ServerSignCert.pem");
HITLS_CERT_X509 *signKey =  LoadKeyFromFile("ServerSignKey.pem");
// 从文件中加载加密证书和私钥
HITLS_CERT_X509 *encCert = LoadCertFromFile("ServerEncCert.pem");
HITLS_CERT_X509 *encKey = LoadKeyFromFile("ServerEncKey.pem");
//设置添加国密签名证书和私钥
HITLS_CFG_SetTlcpCertificate(config, signCert, false, false);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, false);
//设置添加国密加密证书和私钥
HITLS_CFG_SetTlcpCertificate(config, signCert, false, true);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, true);
...

TLS会话密钥更新示例

会话密钥更新类型

(D)TLS1.2/TLCP重协商示例

(D)TLS1.2/TLCP支持安全重协商,重协商功能允许客户端或服务端在同一个安全连接上发起新的协商,以产生新的密钥,一般应用在保密要求高,一条连接上传送数据量大的连接上。 安全重协商流程如下:

image

注意:用户通过HITLS_Renegotiate接口进入重协商状态,可以通过HITLS_AcceptHITLS_ConnectHITLS_WriteHITLS_Read触发重协商握手流程。推荐使用HITLS_AcceptHITLS_Connect发起重协商握手。

客户端示例

/* 应用层数据交互 */
const uint8_t sndBuf[] = "Hi, this is 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;
}
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;
}
/* 客户端进入重协商状态 */
ret = HITLS_Renegotiate(ctx);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Renegotiate error:error code:%d\n", ret);
	goto EXIT;
}
/* 客户端发起重协商握手,服务端则可以通过HITLS_Read进行处理 */
ret = HITLS_Connect(ctx);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Connect failed, ret = 0x%x.\n", ret);
	goto EXIT;
}
/* 重协商结束继续应用层交互 */

服务端示例

/* 应用层数据交互 */
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;
}
const uint8_t sndBuf[] = "Hi, this is 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;
}
/* 服务端进入重协商状态 */
ret = HITLS_Renegotiate(ctx);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Renegotiate error:error code:%d\n", ret);
	goto EXIT;
}
/* 服务端发起重协商握手,客户端则可以通过HITLS_Read进行处理 */
ret = HITLS_Accept(ctx);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Accept failed, ret = 0x%x.\n", ret);
	goto EXIT;
}
/* 重协商结束继续应用层交互 */

TLS1.3密钥更新示例

TLS1.3支持建链后密钥更新能力,其涉及函数如下:

/**
 * @brief   设置keyUpdate类型,并发送keyUpdate消息给对端
 */
int32_t HITLS_KeyUpdate(HITLS_Ctx *ctx, uint32_t updateType);

其支持两种KeyUpdate类型,包括:

HITLS_UPDATE_NOT_REQUESTED = 0, // 要求对端不回复keyUpdate消息
HITLS_UPDATE_REQUESTED = 1,     // 要求对端回复KeyUpdate消息

客户端示例

/* 应用层数据交互 */
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;
}
const uint8_t sndBuf[] = "Hi, this is 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;
}
/* 客户端发起KeyUpdate消息,要求对端不回复,对端则通过HITLS_Read进行处理 */
ret = HITLS_KeyUpdate(ctx, HITLS_UPDATE_NOT_REQUESTED);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_KeyUpdate error:error code:%d\n", ret);
	goto EXIT;
}
/* keyUpdate完毕 */

服务端示例

/* 应用层数据交互 */
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;
}
const uint8_t sndBuf[] = "Hi, this is server\n";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Write error:error code:%d\n", ret);
	goto EXIT;
}
/* 服务端发起KeyUpdate消息,要求对端回复,对端通过HITLS_Read进行处理,并回复KeyUpdate消息 */
ret = HITLS_KeyUpdate(ctx, HITLS_UPDATE_REQUESTED);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_KeyUpdate error:error code:%d\n", ret);
	goto EXIT;
}
/* HITLS_Read中处理对端回复KeyUpdate消息 */
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
	printf("HITLS_Read failed, ret = 0x%x.\n", ret);
	goto EXIT;
}
/* keyUpdate完毕 */