跳到主要内容

TLS特性介绍

协议功能介绍

openHiTLS基于传输层安全协议标准提供了安全协议链路创建、配置、管理等功能,主要功能接口在协议模块中提供。openHiTLS支持多种协议版本以及协议特性功能,包括基础协议握手、密钥更新、应用层协议协商、服务器名称指示等能力。

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_SetExtenedMasterSecretSupport(默认开启)
是否支持sessionTicketHITLS_CFG_SetSessionTicketSupport(默认开启)
是否校验证书keyUsageHITLS_CFG_SetCloseCheckKeyUsage(默认开启)
是否支持自动生成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)
椭圆曲线secp256r1(23)
secp384r1(24)
secp521r1(25)
x25519(29)
ffdhe2048(256)
ffdhe3072(257)
ffdhe4096(258)
ffdhe6144(259)
ffdhe8192(260)
签名哈希算法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_SetExtenedMasterSecretSupport(默认开启)
是否支持sessionTicketHITLS_CFG_SetSessionTicketSupport(默认开启)
是否校验证书keyUsageHITLS_CFG_SetCloseCheckKeyUsage(默认开启)
是否支持自动生成DH参数HITLS_CFG_SetDhAutoSupport(默认开启)

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(默认关闭)
是否校验证书keyUsageHITLS_CFG_SetCloseCheckKeyUsage(默认开启)

扩展能力

扩展类型名称DTLS1.2TLS1.2TLS1.3TLCP
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对部分算法实现,证书解析的依赖随着产品的需求不同而不同,所以对外提供了注册接口,供产品根据自己的实际情况注册对应的接口。openHiTLS依赖接口如下,接口详细参数介绍,请参考API手册头文件:

注册算法相关回调函数:

/** 
* @brief 要求必须注册的回调函数
*/
typedef struct {
CRYPT_RandBytesCallback randBytes; /* 获取随机数 */
CRYPT_HmacSizeCallback hmacSize; /* HMAC:根据hash算法获取HMAC长度 */
CRYPT_HmacInitCallback hmacInit; /* HMAC:初始化上下文 */
CRYPT_HmacFreeCallback hmacFree; /* HMAC:释放上下文 */
CRYPT_HmacUpdateCallback hmacUpdate; /* HMAC:添加输入数据 */
CRYPT_HmacFinalCallback hmacFinal; /* HMAC:输出结果 */
CRYPT_HmacCallback hmac; /* HMAC:单次HMAC函数 */
CRYPT_DigestSizeCallback digestSize; /* HASH:获取哈希长度 */
CRYPT_DigestInitCallback digestInit; /* HASH:初始化上下文 */
CRYPT_DigestCopyCallback digestCopy; /* HASH:复制hash上下文 */
CRYPT_DigestFreeCallback digestFree; /* HASH:释放上下文 */
CRYPT_DigestUpdateCallback digestUpdate; /* HASH:添加输入数据 */
CRYPT_DigestFinalCallback digestFinal; /* HASH:输出hash结果 */
CRYPT_DigestCallback digest; /* HASH:单次hash函数 */
CRYPT_EncryptCallback encrypt; /* TLS加密:为record提供加密能力 */
CRYPT_DecryptCallback decrypt; /* TLS解密:为record提供解密能力 */
} HITLS_CRYPT_BaseMethod;

/**
* @brief ECDH需要注册的回调函数
*/
typedef struct {
CRYPT_GenerateEcdhKeyPairCallback generateEcdhKeyPair; /**<; ECDH:根据椭圆曲线参数,生成密钥对 */
CRYPT_FreeEcdhKeyCallback freeEcdhKey; /**<; ECDH:释放椭圆曲线密钥 */
CRYPT_GetEcdhEncodedPubKeyCallback getEcdhPubKey; /**<; ECDH:提取公钥数据 */
CRYPT_CalcEcdhSharedSecretCallback calcEcdhSharedSecret; /**<; ECDH:根据本端密钥和对端公钥数据,计算共享密钥 */
} HITLS_CRYPT_EcdhMethod;

/**
* @brief DH需要注册的回调函数
*/
typedef struct {
CRYPT_GenerateDhKeyBySecbitsCallback generateDhKeyBySecbits; /* DH:根据secbits,生成密钥对 */
CRYPT_GenerateDhKeyByParamsCallback generateDhKeyByParams; /* DH:根据dh参数,生成密钥对 */
CRYPT_FreeDhKeyCallback freeDhKey; /* DH:释放密钥 */
CRYPT_DHGetParametersCallback getDhParameters; /* DH:通过密钥句柄获取p g plen glen */
CRYPT_GetDhEncodedPubKeyCallback getDhPubKey; /* DH:提取Dh公钥数据 */
CRYPT_CalcDhSharedSecretCallback calcDhSharedSecret; /* DH:根据本端密钥和对端公钥数据,计算共享密钥 */
} HITLS_CRYPT_DhMethod;

/**
* @brief KDF函数需要注册回调函数
*/
typedef struct {
CRYPT_HkdfExtractCallback hkdfExtract;
CRYPT_HkdfExpandCallback hkdfExpand;
} HITLS_CRYPT_KdfMethod;

/**
* @brief 注册基本的回调函数
*/
int32_t HITLS_CRYPT_RegisterBaseMethod(HITLS_CRYPT_BaseMethod *userCryptCallBack);

/**
* @brief 注册ECDH的回调函数
*/
int32_t HITLS_CRYPT_RegisterEcdhMethod(HITLS_CRYPT_EcdhMethod *userCryptCallBack);

/**
* @brief 注册DH的回调函数
*/
int32_t HITLS_CRYPT_RegisterDhMethod(const HITLS_CRYPT_DhMethod *userCryptCallBack);

/**
* @brief 注册Hkdf的回调函数
*/
int32_t HITLS_CRYPT_RegisterHkdfMethod(HITLS_CRYPT_KdfMethod *userCryptCallBack);

注册证书相关回调函数:

/** 
* @brief 要求必须注册的回调函数
*/
typedef struct {
CERT_StoreNewCallBack certStoreNew; /* 创建证书store */
CERT_StoreDupCallBack certStoreDup; /* 复制证书store */
CERT_StoreFreeCallBack certStoreFree; /* 释放证书store */
CERT_StoreCtrlCallBack certStoreCtrl; /* 证书store ctrl接口 */
CERT_BuildCertChainCallBack buildCertChain; /* 构造证书链 */
CERT_VerifyCertChainCallBack verifyCertChain; /* 验证证书链 */

CERT_CertEncodeCallBack certEncode; /* 证书编码 */
CERT_CertParseCallBack certParse; /* 证书解码 */
CERT_CertDupCallBack certDup; /* 深拷贝证书 */
CERT_CertRefCallBack certRef; /* 证书引用计数加一 */
CERT_CertFreeCallBack certFree; /* 释放证书 */
CERT_CertCtrlCallBack certCtrl; /* 证书ctrl接口 */

CERT_KeyParseCallBack keyParse; /* 解析key */
CERT_KeyDupCallBack keyDup; /* 深拷贝key */
CERT_KeyFreeCallBack keyFree; /* 释放key */
CERT_KeyCtrlCallBack keyCtrl; /* key ctrl接口 */
CERT_CreateSignCallBack createSign; /* 签名 */
CERT_VerifySignCallBack verifySign; /* 验签 */
CERT_EncryptCallBack encrypt; /* 密钥交换加密 */
CERT_DecryptCallBack decrypt; /* 密钥交换解密 */

CERT_CheckPrivateKeyCallBack checkPrivateKey; /* 检查证书和key是否匹配 */
} HITLS_CERT_MgrMethod;

/**
* @brief 注册证书相关回调函数
*/
int32_t HITLS_CERT_RegisterMgrMethod(HITLS_CERT_MgrMethod *method);

/**
* @brief 注销证书相关回调函数
*/
void HITLS_CERT_DeinitMgrMethod(void);

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

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 (memcpy_s(identity, maxIdentityLen, "hello", strlen("hello") + 1) != EOK) {
return 0;
}
if (memcpy_s(psk, maxPskLen, pskTrans, pskTransUsedLen) != EOK) {
return 0;
}
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 (memcpy_s(psk, maxPskLen, pskTrans, pskTransUsedLen) != EOK) {
return 0;
}
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, ture) != 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";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
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";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
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";
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进行处理 */
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完毕 */

欢迎关注openHiTLS微信公众号