商密TLCP协议浅析——通过代码和GM/T38636 — 2020分析主要流程

2,225 阅读10分钟

上一篇简述TLCP抓包的情况,很多在细节在抓包的层面并没有很好的体现出来。所以本文将简述通过对gmssl的tlcp部分代码进行分析,以及结合《GB∕T 38636-2020 信息安全技术 传输层密码协议(TLCP)》这个国家标准,对TLCP一些遗漏的细节进行补充,特别是双证书机制的部分。

sequenceDiagram
client->>server: 1、Client Hello
server->>client: 2、Server Hello
server->>client: 3、Certificate
server->>client: 4、Server Key Exchange
server->>client: 5、Server Hello Done
client->>server: 6、Client Key Exchange
client->>server: 7、Change Cipher Spec, Encrypted Handshake Message
server->>client: 8、Change Cipher Spec
server->>client: 9、Encrypted Handshake Message
gmssl tlcp_server -port 4433 -cert double_certs.pem -key signkey.pem -pass 1234 -ex_key enckey.pem -ex_pass 1234

gmssl tlcp_client -host 127.0.0.1 -port 4433

tlcp server的启动

tlcp上下文的初始化,将传入的证书整合进TCLP_CTX这个struct中

typedef struct {
	int protocol;
	int is_client;
	int cipher_suites[TLS_MAX_CIPHER_SUITES_COUNT];
	size_t cipher_suites_cnt;
	uint8_t *cacerts;
	size_t cacertslen;
	uint8_t *certs;
	size_t certslen;
	SM2_KEY signkey;
	SM2_KEY kenckey;
	int verify_depth;
} TLS_CTX;
......
if (tls_ctx_init(&ctx, TLS_protocol_tlcp, TLS_server_mode) != 1
		|| tls_ctx_set_cipher_suites(&ctx, server_ciphers, sizeof(server_ciphers)/sizeof(int)) != 1
		|| tls_ctx_set_tlcp_server_certificate_and_keys(&ctx, certfile, signkeyfile, signpass, enckeyfile, encpass) != 1) {
		error_print();
		return -1;
	}

这里详细看一下TLS_CTX这个结构体

  • int protocol

这里协议就是指Record Layer的Version字段为(0x0101)

  • int cipher_suites[TLS_MAX_CIPHER_SUITES_COUNT];size_t cipher_suites_cnt;

支持的套件列表,后续进行加密套件协商的时候需要用到

  • uint8_t *cacerts;size_t cacertslen;int verify_depth;

CA证书,启用双向认证后用来对客户端的证书进行校验

  • uint8_t *certs;size_t certslen;

从double_certs.pem中读取到的证书。

  • SM2_KEY signkey;

从signkey.pem结合密码获取sm2私钥,用来对通信端的身份进行验证。

  • SM2_KEY kenckey;

从enckey.pem结合密码获取sm2私钥,该私钥为tlcp协议独有,用于密钥交换。

tlcp_server建立tcp连接后

接收到客户端的tcp请求后,将accept后的socket与tlcp关联,这里就主要考虑到TLS_CONNECT这个struct。

typedef struct {
	int protocol;
	int is_client;
	int cipher_suites[TLS_MAX_CIPHER_SUITES_COUNT];
	size_t cipher_suites_cnt;

	int sock;

	uint8_t enced_record[TLS_MAX_RECORD_SIZE];
	size_t enced_record_len;


	uint8_t record[TLS_MAX_RECORD_SIZE];

	uint8_t databuf[TLS_MAX_PLAINTEXT_SIZE];
	uint8_t *data;
	size_t datalen;

	int cipher_suite;
	uint8_t session_id[32];
	size_t session_id_len;
	uint8_t server_certs[TLS_MAX_CERTIFICATES_SIZE];
	size_t server_certs_len;
	uint8_t client_certs[TLS_MAX_CERTIFICATES_SIZE];
	size_t client_certs_len;
	uint8_t ca_certs[2048];
	size_t ca_certs_len;

	SM2_KEY sign_key;
	SM2_KEY kenc_key;

	int verify_result;

	uint8_t master_secret[48];
	uint8_t key_block[96];

	SM3_HMAC_CTX client_write_mac_ctx;
	SM3_HMAC_CTX server_write_mac_ctx;
	SM4_KEY client_write_enc_key;
	SM4_KEY server_write_enc_key;
	uint8_t client_seq_num[8];
	uint8_t server_seq_num[8];

	uint8_t client_write_iv[12]; // tls13
	uint8_t server_write_iv[12]; // tls13
	BLOCK_CIPHER_KEY client_write_key;
	BLOCK_CIPHER_KEY server_write_key;

} TLS_CONNECT;、

......

if (tls_init(&conn, &ctx) != 1
		|| tls_set_socket(&conn, conn_sock) != 1) {
		error_print();
		return -1;
	}

	if (tls_do_handshake(&conn) != 1) {
		error_print();
		return -1;
	}

conn整合ctx的成员。接下来就是重点tlcp的握手部分,主要在int tlcp_do_accept(TLS_CONNECT *conn)和int tlcp_do_connect(TLS_CONNECT *conn)这两个函数。

TLCP握手分析

记录层定义

记录层接受从高层来的任意来的任意大小的非空连续数据,将数据分段、压缩、计算校验码、加密,然后传输。接收到的数据经过解密、验证、解压缩、重新封装然后传送给高层应用。

GB/T 38636-2020(接下来简称GB)中规定记录层结构:

struct {
    Content Type;
    ProtocoVersion version;
    uint16 length;
    opaque fragment[TLSPlainttext,length];
} TLSPlaintext;

Type是片段的记录层协议类型,定义为
enum {
    change_cipher_spec(20),
    alert(21),
    handshake(22),
    application_data(23),(255)
}

Version所用协议的版本号。这里的版本号为1.1,定义为
struct{
    uint8 major=0x01,
    uint8 minor=0x01;
} ProtocolVersion;

length以字节为单位的片段长度,小于或等于2^14

fragment是将传输的数据,记录层协议版不关心具体数据内容。

握手协议定义

GB中对于握手协议的结构定义 握手协议是在记录协议之上的协议,用于协商安全参数。握手协议的消息通过记录层协议传输。 握手消息结构定义:

struct {
    HandshakeType msg_type;
    uint24 length;
    select (msg_type){
        case client_hello: ClientHello;
        case server_hello: ServerHello;
        case certifivate: Certificate;
        case server_key_exchange: ServerKeyExchange;
        case certificate_request: CertificateRequest;
        case server_hello_done: ServerHelloDone;
        case certificate_verify: CertificateVerify;
        case client_key_exchange: ClientKeyExchange;
        case finished: Finished;
    } body;
}Handshake;

握手消息类型定义如下:
enum{
    client_hello(1),
    server_hello(2),
    certificate(11),
    server_key_exchange(12),
    certificate_request(13),
    server_hello_done(14),
    certificate_verify(15),
    client_key_exchange(16)
}
  1. client:send Client Hello

GB规定的Client Hello消息

struct {
    Protocol Version client_version ;
    Randomrandom;
    SessionID session_id ;
    CipherSuite cipher_suites 〈 2..2^16-1 〉;
    CompressionMethod compression_methods 〈 1..2^8-1 〉;
} ClientHello ;
tls_random_generate(client_random);
if (tls_record_set_handshake_client_hello(record, &recordlen,
        TLS_protocol_tlcp, client_random, NULL, 0,
        tlcp_ciphers, tlcp_ciphers_count, NULL, 0) != 1) {
        error_print();
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

生成随机数后通过tls_record_set_handshake_client_hello函数组成握手信息,将client_hello 信息封装成记录层。封装完成后,将携带client_hello的握手消息的记录层发送给服务端。

对比抓包情况一致。 Screenshot_20230202_131721.png

  1. server: receive Client Hello;send Server Hello;

接收client hello进行处理

// 接收记录层消息
if (tls_record_recv(record, &recordlen, conn->sock) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
// 校验记录层协议
if (tls_record_protocol(record) != TLS_protocol_tlcp) {
        error_print();
        tls_send_alert(conn, TLS_alert_protocol_version);
        goto end;
}
// 从记录层解码出Client Hello握手消息
if (tls_record_get_handshake_client_hello(record,
        &protocol, &random, &session_id, &session_id_len,
        &client_ciphers, &client_ciphers_len,
        &exts, &exts_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
//校验握手协议TLCP版本
if (protocol != TLS_protocol_tlcp) {
        error_print();
        tls_send_alert(conn, TLS_alert_protocol_version);
        goto end;
}
memcpy(client_random, random, 32);
// 协商密码套件
if (tls_cipher_suites_select(client_ciphers, client_ciphers_len,
        server_ciphers, sizeof(server_ciphers)/sizeof(server_ciphers[0]),
        &conn->cipher_suite) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_insufficient_security);
        goto end;
}

主要逻辑在于消息解码(tls_record_get_handshake_client_hello函数)和套件协商(tls_cipher_suites_select)

发送Server Hello

GB规定的Server Hello消息应当协商出密码套件,服务端发送这个消息作为客户端hello消息的回复。


struct {
    ProtocolVersionserver _ version ;
    Randomrandom;
    SessionID session_id ;
    CipherSuite cipher_suite ;
    CompressionMethod compression_method ;
} ServerHello ;

代码实现

tls_random_generate(server_random);
if (tls_record_set_handshake_server_hello(record, &recordlen,
        TLS_protocol_tlcp, server_random, NULL, 0,
        conn->cipher_suite, NULL, 0) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

生成随机数,将随机数和协商出的密码套件封装成握手消息,放在记录层发送给客户端。

3.client: receive Client Hello

if (tls_record_recv(record, &recordlen, conn->sock) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
if (tls_record_get_handshake_server_hello(record,
        &protocol, &random, &session_id, &session_id_len, &cipher_suite,
        &exts, &exts_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
if (tls_cipher_suite_in_list(cipher_suite, tlcp_ciphers, tlcp_ciphers_count) != 1) {
        tls_send_alert(conn, TLS_alert_handshake_failure);
        error_print();
        goto end;
}
conn->cipher_suite = cipher_suite;

解码消息,获取服务端的随机数以及加密套件。

  1. server:Certificate

服务端证书消息

服务端应发送一个服务端证书消息给客户端,该消息总是紧跟在服务端hello消息之后。当选中的密码套件使用RSA或ECC或ECDHE算法时,本消息的内容为服务端签名证书和加密证书;当选中的密码套件使用IBC或IBSDH算法时,本消息的内容为服务端标识和IBC公共参数,用于客户端与服务端协商IBC公开参数。

证书消息结构

opaqueASN.1Cert 〈 1..2^24-1 〉;
struct {
    ASN.1Certcertificate 〈 0..2^24-1 〉;
}Certificate;

其中签名证书在前,加密证书在后。

代码实现

if (tls_record_set_handshake_certificate(record, &recordlen,
        conn->server_certs, conn->server_certs_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

int tls_record_set_handshake_certificate(uint8_t *record, size_t *recordlen,const uint8_t *certs, size_t certslen){
......
while (certslen) {
        const uint8_t *cert;
        size_t certlen;

        if (x509_cert_from_der(&cert, &certlen, &certs, &certslen) != 1) {
                error_print();
                return -1;
        }
        tls_uint24array_to_bytes(cert, certlen, NULL, &datalen);
        if (datalen > TLS_MAX_HANDSHAKE_DATA_SIZE) {
                error_print();
                return -1;
        }
        tls_uint24array_to_bytes(cert, certlen, &p, &len);
}
......
}

从certs中抽取所有证书放入记录层,所以从抓包来看有三张证书

Screenshot_20230202_150148.png

  1. client: receive Certificate
if (tls_record_recv(record, &recordlen, conn->sock) != 1
        || tls_record_protocol(record) != TLS_protocol_tlcp) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
if (tls_record_get_handshake_certificate(record,
        conn->server_certs, &conn->server_certs_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}

tls_record_get_handshake_certificate函数直接解析了证书链。

  1. server: Server Key Exchange

服务端密钥交换消息。本消息传送的信息用于客户端计算产生48字节的预主密钥。

服务端密钥交换消息的结构

enum { ECDHE , ECC , IBSDH, IBC , RSA } KeyExchangeAlgorithm;
struct {
select( KeyExchangeAlgorithm ){
    ......
    case ECC :
        digitally-signed struct {
            opaqueclient_random [ 32 ];
            opaqueserver_random[ 32 ];
            opaqueASN.1Cert 〈 1..2^24-1 〉;
        } signed_params 
    ......
}

这里使用的是ECC,我们就只看ECC部分。当密钥交换方式为ECC和RSA时,signed_params是服务端对双方随机数和服务端加密证书的签名。

代码实现


if (x509_certs_get_cert_by_index(conn->server_certs, conn->server_certs_len, 1,
        &server_enc_cert, &server_enc_cert_len) != 1) {
        error_print();
        goto end;
}
p = server_enc_cert_lenbuf; len = 0;
tls_uint24_to_bytes(server_enc_cert_len, &p, &len);
if (sm2_sign_init(&sign_ctx, &conn->sign_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1
        || sm2_sign_update(&sign_ctx, client_random, 32) != 1
        || sm2_sign_update(&sign_ctx, server_random, 32) != 1
        || sm2_sign_update(&sign_ctx, server_enc_cert_lenbuf, 3) != 1
        || sm2_sign_update(&sign_ctx, server_enc_cert, server_enc_cert_len) != 1
        || sm2_sign_finish(&sign_ctx, sigbuf, &siglen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tlcp_record_set_handshake_server_key_exchange_pke(record, &recordlen, sigbuf, siglen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
tlcp_record_trace(stderr, record, recordlen, 0, 0);
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

通过sm2_sign_update将client_random,server_random,server_enc_cert_lenbuf,server_enc_cert等做sm3处理,将最后得出的密钥使用sign_cert中的私钥进行sm2进行加密。将加密后的预主密钥发送到客户端。

  1. client: receive Server Key Exchange
// 接收加密后预主密钥
if (tlcp_record_get_handshake_server_key_exchange_pke(record, &sig, &siglen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}

//获取公钥
if (x509_certs_get_cert_by_index(conn->server_certs, conn->server_certs_len, 0, &cp, &len) != 1
        || x509_cert_get_subject_public_key(cp, len, &server_sign_key) != 1
        || x509_certs_get_cert_by_index(conn->server_certs, conn->server_certs_len, 1, &server_enc_cert, &server_enc_cert_len) != 1
        || x509_cert_get_subject_public_key(server_enc_cert, server_enc_cert_len, &server_enc_key) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_bad_certificate);
        goto end;
}
p = server_enc_cert_lenbuf; len = 0;
tls_uint24_to_bytes(server_enc_cert_len, &p, &len);
// 校验预主密钥
if (sm2_verify_init(&verify_ctx, &server_sign_key, SM2_DEFAULT_ID, SM2_DEFAULT_ID_LENGTH) != 1
        || sm2_verify_update(&verify_ctx, client_random, 32) != 1
        || sm2_verify_update(&verify_ctx, server_random, 32) != 1
        || sm2_verify_update(&verify_ctx, server_enc_cert_lenbuf, 3) != 1
        || sm2_verify_update(&verify_ctx, server_enc_cert, server_enc_cert_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (sm2_verify_finish(&verify_ctx, sig, siglen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_decrypt_error);
        goto end;
}

从之前下发的证书链中解析出身份验证证书中,并获取解密公钥;也解析出sign证书,并获取公钥。 和服务端一样将四个参数进行sm3处理。将接收加密的预主密钥进行解密,并与sm3获取的值进行对比。本质上就是一个验签过程。

  1. server: Server Hello Done

表示握手过程的hello消息阶段完成。发送完该消息后服务端会等待客户端的响应消息。

  1. client: receive Server Hello Done;Client Key Exchange;

客户端接收到服务端的服务端的hello完成消息之后,主要就是校验服务端的hello消息参数是否可以接受。正常将继续接下来的握手协议,Client Key Exchange步骤。

如果密钥算法使用RSA算法、ECC算法和IBC算法,本消息中包含预主密钥,该预主密钥由客户端产生,采用服务端的加密公钥进行加密。

客户端密钥交换消息结构定义如下:

struct {
    select( KeyExchangeAlgorithm ){
    ......
    caseECC :
        opaqueECCEncryptedPreMasterSecret 〈 0..2^16-1 〉;
    ......
    }
}

预主密钥的数据结构

struct {
    ProtocolVersion client_version ;
    opaque random[ 46 ];
} PreMasterSecret ;

代码实现

//生成主密钥
if (tls_pre_master_secret_generate(pre_master_secret, TLS_protocol_tlcp) != 1
        || tls_prf(pre_master_secret, 48, "master secret",
                client_random, 32, server_random, 32,
                48, conn->master_secret) != 1
        || tls_prf(conn->master_secret, 48, "key expansion",
                server_random, 32, client_random, 32,
                96, conn->key_block) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
sm3_hmac_init(&conn->client_write_mac_ctx, conn->key_block, 32);
sm3_hmac_init(&conn->server_write_mac_ctx, conn->key_block + 32, 32);
sm4_set_encrypt_key(&conn->client_write_enc_key, conn->key_block + 64);
sm4_set_decrypt_key(&conn->server_write_enc_key, conn->key_block + 80);

if (sm2_encrypt(&server_enc_key, pre_master_secret, 48,
                enced_pre_master_secret, &enced_pre_master_secret_len) != 1
        || tls_record_set_handshake_client_key_exchange_pke(record, &recordlen,
                enced_pre_master_secret, enced_pre_master_secret_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

通过随机生成随机生成的pre_master_secret,将server_random和client_random作为随机种子去生成主密钥。将预主密钥用密钥交换算法的公钥加密发送给服务端。这里就是与普通TLS最大的不同,会有另外一个公钥将客户端的预主密钥进行加密。

  1. server: receive Client Key Exchange
if (tls_record_get_handshake_client_key_exchange_pke(record, &enced_pms, &enced_pms_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
if (sm2_decrypt(&conn->kenc_key, enced_pms, enced_pms_len,
        pre_master_secret, &pre_master_secret_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_decrypt_error);
        goto end;
}
if (pre_master_secret_len != 48) {
        error_print();
        tls_send_alert(conn, TLS_alert_decrypt_error);
        goto end;
}

读取被客户端加密后的预主密钥,并进行解密获取。

  1. client: Change Cipher Spec, Encrypted Handshake Message
// Change Cipher Spec
if (tls_record_set_change_cipher_spec(record, &recordlen) !=1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

//
if (tls_prf(conn->master_secret, 48, "client finished",sm3_hash, 32, NULL, 0, sizeof(local_verify_data), local_verify_data) != 1
        || tls_record_set_handshake_finished(finished_record, &finished_record_len,
        local_verify_data, sizeof(local_verify_data)) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}

if (tls_record_encrypt(&conn->client_write_mac_ctx, &conn->client_write_enc_key,
		conn->client_seq_num, finished_record, finished_record_len, record, &recordlen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
tls_seq_num_incr(conn->client_seq_num);
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

发送Change Cipher Spec,并发送一个加密后的消息,加密消息为client finished。

  1. server: receive Change Cipher Spec, Encrypted Handshake Message;Change Cipher Spec

if (tls_prf(pre_master_secret, 48, "master secret",
                client_random, 32, server_random, 32,
                48, conn->master_secret) != 1
        || tls_prf(conn->master_secret, 48, "key expansion",
                server_random, 32, client_random, 32,
                96, conn->key_block) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
sm3_hmac_init(&conn->client_write_mac_ctx, conn->key_block, 32);
sm3_hmac_init(&conn->server_write_mac_ctx, conn->key_block + 32, 32);
sm4_set_decrypt_key(&conn->client_write_enc_key, conn->key_block + 64);
sm4_set_encrypt_key(&conn->server_write_enc_key, conn->key_block + 80);

//接收 ChangeCipherSpec消息
if (tls_record_recv(record, &recordlen, conn->sock) != 1
        || tls_record_protocol(record) != TLS_protocol_tlcp) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}
if (tls_record_get_change_cipher_spec(record) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_unexpected_message);
        goto end;
}

//解码
if (tls_record_decrypt(&conn->client_write_mac_ctx, &conn->client_write_enc_key,
		conn->client_seq_num, record, recordlen, finished_record, &finished_record_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_bad_record_mac);
        goto end;
}

tls_seq_num_incr(conn->client_seq_num);
if (tls_record_get_handshake_finished(finished_record, &verify_data, &verify_data_len) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_bad_record_mac);
        goto end;
}
if (verify_data_len != sizeof(local_verify_data)) {
        error_print();
        tls_send_alert(conn, TLS_alert_bad_record_mac);
        goto end;
}

memcpy(&tmp_sm3_ctx, &sm3_ctx, sizeof(SM3_CTX));
sm3_update(&sm3_ctx, finished_record + 5, finished_record_len - 5);
sm3_finish(&tmp_sm3_ctx, sm3_hash);
if (tls_prf(conn->master_secret, 48, "client finished", sm3_hash, 32, NULL, 0,
        sizeof(local_verify_data), local_verify_data) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (memcmp(verify_data, local_verify_data, sizeof(local_verify_data)) != 0) {
        error_puts("client_finished.verify_data verification failure");
        tls_send_alert(conn, TLS_alert_decrypt_error);
        goto end;
}

if (tls_record_set_change_cipher_spec(record, &recordlen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

通过与客户端相同的方式生成主密钥

对接收到加密包进行验证。

发送服务端的Change Cipher Spec

  1. client: receive Change Cipher Spec

同服务端处理差不多,不赘述。

  1. server: Encrypted Handshake Message

sm3_finish(&sm3_ctx, sm3_hash);
if (tls_prf(conn->master_secret, 48, "server finished", sm3_hash, 32, NULL, 0,
                sizeof(local_verify_data), local_verify_data) != 1
        || tls_record_set_handshake_finished(finished_record, &finished_record_len,
                local_verify_data, sizeof(local_verify_data)) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}
if (tls_record_encrypt(&conn->server_write_mac_ctx, &conn->server_write_enc_key,
        conn->server_seq_num, finished_record, finished_record_len, record, &recordlen) != 1) {
        error_print();
        tls_send_alert(conn, TLS_alert_internal_error);
        goto end;
}

tls_seq_num_incr(conn->server_seq_num);
if (tls_record_send(record, recordlen, conn->sock) != 1) {
        error_print();
        goto end;
}

服务端同客户端类似,对server finished进行加密传输。

  1. client: receive Encrypted Handshake Message

同服务端处理差不多,不赘述。

  1. client:开始进行加密数据传输
if (tls_record_set_type(record, TLS_record_application_data) != 1
        || tls_record_set_protocol(record, conn->protocol) != 1
        || tls_record_set_length(record, inlen) != 1) {
        error_print();
        return -1;
}
if (tls_cbc_encrypt(hmac_ctx, enc_key, seq_num, tls_record_header(record),
        in, inlen, tls_record_data(record), &datalen) != 1) {
        error_print();
        return -1;
}
if (tls_record_set_length(record, datalen) != 1) {
        error_print();
        return -1;
}
tls_seq_num_incr(seq_num);
if (tls_record_send(record, tls_record_length(record), conn->sock) != 1) {
        error_print();
        return -1;
}

做好记录层的准备,将要传输的数据sm4和cbc进行处理加密

  1. server:接收加密数据并进行解密。
if ((ret = tls_record_recv(record, &recordlen, conn->sock)) != 1) {
        if (ret < 0) error_print();
        return ret;
}
if (tls_cbc_decrypt(hmac_ctx, dec_key, seq_num, record,
        tls_record_data(record), tls_record_data_length(record),
        conn->databuf, &conn->datalen) != 1) {
        error_print();
        return -1;
}
conn->data = conn->databuf;
tls_seq_num_incr(seq_num);

tls_record_set_data(record, conn->data, conn->datalen);

接收记录层,从记录层解析出加密数据,并进行解密。

至此,整个流程解析完毕。