最近需要做一些加解密的事情,简单分析了一下TLS安全机制在OpenSSL中的实现,记录一下。

  • 上篇介绍 TLS 1.2, 加密套件选取ECDHE_RSA_AES128_GCM_SHA256.
  • 下篇介绍 TLS 1.3, 加密套件选取TLS_AES_256_GCM_SHA384

这两种协议及其加密套件覆盖了主流网站采用的 TLS 安全机制。

1. TLS 安全机制介绍

TLS 是一种用于在互联网上进行安全通信的协议,其主要功能包括:

  • 身份验证:使用数字证书和公钥基础设施(PKI)来验证通信双方的身份。
  • 密钥交换:使用非对称加密算法(如ECDHE, RSA)来交换对称加密算法的密钥。
  • 加密:使用对称加密算法(如 AES)和非对称加密算法(如 RSA)来保护数据传输的机密性。
  • 数据完整性:使用消息认证码(如 HMAC)来确保数据在传输过程中未被篡改。

2. TLS 加密套件选择

  • 获取top100热门网站的服务端首选加密套件,计各个加密套件的占比情况。

    TLS协议版本 加密套件 个数 占比
    TLSv1.3 TLS_AES_256_GCM_SHA384 41 41%
    TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256 28 28%
    TLSv1.2 ECDHE_RSA_CHACHA20_POLY1305 13 13%
    TLSv1.2 ECDHE_RSA_AES256_GCM_SHA384 10 10%
    TLSv1.2 ECDHE_ECDSA_AES128_GCM_SHA256 4 4%
    TLSv1.3 TLS_AES_128_GCM_SHA256 3 3%
  • top10网站的加密套件

    网站名称 网址 TLS协议版本 加密套件
    百度 www.baidu.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256
    腾讯网 www.qq.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256
    搜狐 www.sohu.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256
    新浪网 www.sina.com.cn TLSv1.3 TLS_AES_256_GCM_SHA384
    好搜 www.so.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256
    网易 www.163.com TLSv1.3 TLS_AES_256_GCM_SHA384
    CSDN www.csdn.net TLSv1.3 TLS_AES_256_GCM_SHA384
    搜狗 www.sohu.com TLSv1.2 ECDHE_RSA_AES256_GCM_SHA384
    哔哩哔哩 www.bilibili.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256
    知乎 www.zhihu.com TLSv1.3 TLS_AES_256_GCM_SHA384
    豆瓣 www.douban.com TLSv1.2 ECDHE_RSA_AES128_GCM_SHA256

    热门网站数据来源https://top.chinaz.com/all/

统计信息显示主流网站主要采用如下TLS的安全机制:

  • 基于TLS 1.2的加密套件ECDHE_RSA_AES128_GCM_SHA256
  • 基于TLS 1.3的加密套件TLS_AES_256_GCM_SHA384

以下分析主要基于这两个加密套件。

2.1 加密套件ECDHE_RSA_AES128_GCM_SHA256

ECDHE_RSA_AES128_GCM_SHA256主要用于 TLS 1.2,可以提供较好的性能和安全性平衡,主要交互流程如下。

 Client                          Server
   |                               |
   |------- ClientHello ---------->|
   |                               |
   |<------ ServerHello -----------|
   |<------ Server Certificate ----|  身份认证, 对服务端证书签名进行校验
   |<------ ServerKeyExchange -----|    
   |<------ ServerHelloDone -------|
   |                               |
   |------ ClientKeyExchange ----->|  密钥交换,默认采用 ECDHE 进行密钥交换,椭圆曲线选用 secp256r1
   |------ CertificateVerify ----->| 
   |------ ChangeCipherSpec ------>|  通过PRF算法派生出pre-master-key, master-key,
   |                               |  最终派生出client_secret和server_secret分别用于客户端和服务端数据加密
   |------ Finished -------------->|
   |                               |
   |<------ ChangeCipherSpec ------|
   |<------ Finished --------------|
   |                               |
   |<------Application  Data ----> | 
   |<------Application  Data ----> | 使用AES128-GCM对数据包进行加密加签
   |                               | 注:在TLS 1.2协议中仅数据包有加密加签,握手消息未加密加签

2.2 加密套件TLS_AES_256_GCM_SHA384

TLS_AES_256_GCM_SHA384基于 TLS 1.3, 提供更强的加密和哈希安全性,适合需要高安全性的场景, 主要交互流程如下:

Client                            Server
  |                                 |
  |------ ClientHello ------------->|
  |                                 |
  |<------ ServerHello -------------|  密钥交换, 默认采用 ECDHE 进行密钥交换,椭圆曲线选用 x25519
  |                                 |  密钥交换完成生成pre-master-secret 
  |<------ ChangeCipherSpec --------|  使用HKDF算法,根据pre-master-secret派生出【握手密钥】对握手消息加密
  |                                 |  握手密钥分为client_handshake_traffic_secret和server_handshake_traffic_secret分别用于客户端和服务端握手消息加密
  |                                 |  
  |                                 |  以下握手消息都是用handshake secret进行加密的
  |<------ Application Data --------|  Server EncryptedExtensions消息
  |<------ Application Data --------|  Server Certificate消息,用于身份认证,验证服务端证书签名
  |<------ Application Data --------|  Server CertificateVerify消息,用于对握手消息进行验签
  |<------ Application Data --------|  Server Finished消息 , TLS 1.3 密钥派生:https://datatracker.ietf.org/doc/html/rfc8446#section-7.1
  |                                 |
  |------ ChangeCipherSpec  ------->|  通过HKDF算法派生出master secret
  |                                 |  最终派生出client_app_traffic_secret, server_app_traffic_secret分别用于客户端和服务端数据加密
  |------ Application Data -------->|  Client Finished消息
  |                                 |
  |<------Application  Data ------> |  使用AES256-GCM对数据包进行加密加签
  |<------Application  Data ------> | 
  |                                 |

3. 基于 ECDHE_RSA_AES128_GCM_SHA256 加密套件的 TLS 安全机制

基于 ECDHE_RSA_AES128_GCM_SHA256 的 TLS 通信过程主要包括以下步骤:

  • 通过数字证书来验证通信双方的身份。
  • 使用ECDHE算法进行密钥交换,生成临时的会话密钥。
  • 使用AES128-GCM算法对数据进行加密,保证数据的机密性
  • 使用SHA256算法对数据进行签名,保证数据的完整性和不可抵赖性。

3.1、身份认证

客户端收到服务端发送的Server Certificate消息后,会对证书中的签名进行校验,验证通过后,密钥交换过程中会使用证书中的公钥信息。

以下主要介绍服务端证书的验证过程,客户端证书的验证过程类似。以 www.baidu.com 网站为例,使用 openssl s_client 命令可以获取到百度的证书链,如下所示:

$ openssl s_client -connect www.baidu.com:443 -servername www.baidu.com -showcerts

   depth=2 OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
   depth=1 C = BE, O = GlobalSign nv-sa, CN = GlobalSign RSA OV SSL CA 2018
   depth=0 C = CN, ST = beijing, L = beijing, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com

证书验证过程分为两部分:

  • 客户端根据本地预置的 CA 证书,对 GlobalSign Root CA 进行合法性检查。
  • 使用 CA 证书的公钥对证书逐级进行签名校验,确保每一级证书的有效性和可信性。

通过以上步骤,客户端能够确认服务端的身份,从而确保后续通信的安全性和可靠性。

3.1.1、根据系统预置的可信CA证书列表,对证书链中的根证书进行合法性检查

以openssl client为例读取本地预置的ROOT CA证书列表, 对GlobalSign Root CA合法性检查。

// openssl在启动的时候,可以指定加载CA证书,命令如下: 
$ openssl s_client -connect www.baidu.com:443 -CAfile /usr/local/etc/openssl@1.1/cert.pem

// CA证书读取的代码调用栈 
libcrypto.1.1.dylib                 0x0000000101ce27d8 PEM_X509_INFO_read_bio + 120
libcrypto.1.1.dylib                 0x0000000101d64a6f X509_load_cert_crl_file + 159
libcrypto.1.1.dylib                 0x0000000101d64d38 by_file_ctrl + 248
libcrypto.1.1.dylib                 0x0000000101d69a55 X509_LOOKUP_ctrl + 181
libcrypto.1.1.dylib                 0x0000000101d6904f X509_STORE_load_locations + 143
libssl.1.1.dylib                    0x000000010170e759 SSL_CTX_load_verify_locations + 41
openssl                             0x000000010155253e ctx_set_verify_locations + 158
openssl                             0x000000010152a5de s_client_main + 12382
openssl                             0x0000000101514a99 do_cmd + 233
openssl                             0x000000010151443b main + 651
dyld                                0x00007ff80337a41f start + 1903

// 读取代码
STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,..) {

    for (;;) {
        i = PEM_read_bio(bp, &name, &header, &data, &len);
        d2i = (D2I_OF(void)) d2i_X509;
        if (strcmp(name, PEM_STRING_X509) == 0) {
            sk_X509_INFO_push(ret, xi)
            xi = X509_INFO_new();
            pp = &(xi->x509);
        }
        PEM_get_EVP_CIPHER_INFO(header, &cipher);
        PEM_do_header(&cipher, data, &len, cb, u);
        const unsigned char* p = data;
        // 把perm文件中的CA证书转换为x509 
        d2i(pp, &p, len);
    }
}

// 部分预置的ROOT CA证书
x509_object_idx i = 0, /C=FR/O=Dhimyotis/CN=Certigna
x509_object_idx i = 1, /O=TeliaSonera/CN=TeliaSonera Root CA v1
x509_object_idx i = 2, /C=ES/O=IZENPE S.A./CN=Izenpe.com
x509_object_idx i = 3, /C=US/O=Amazon/CN=Amazon Root CA 1
x509_object_idx i = 4, /C=US/O=Amazon/CN=Amazon Root CA 2
x509_object_idx i = 5, /C=US/O=Amazon/CN=Amazon Root CA 3
... 
x509_object_idx i = 70, /C=CN/O=China Financial Certification Authority/CN=CFCA EV ROOT
x509_object_idx i = 71, /C=US/OU=emSign PKI/O=eMudhra Inc/CN=emSign Root CA - C1
x509_object_idx i = 72, /C=BE/O=GlobalSign nv-sa/OU=Root CA/CN=GlobalSign Root CA

3.2.2、 证书校验, 用CA的公钥对证书签名进行验签

证书校验的核心逻辑位于 internal_verify 函数中,证书的验证签名算法采用的是 sha256WithRSAEncryption,签名验证的具体实现细节在 int_rsa_verify 函数中。

  • internal_verify:该函数负责各级证书的合法性检查。
  • sha256WithRSAEncryption:证书签名算法,使用 SHA-256 进行哈希处理,然后使用 RSA 算法进行加密。
  • int_rsa_verify:具体执行签名验证操作。它接收签名、原始数据和公钥,使用 RSA 解密签名并比对哈希值,确认签名的合法性。

调用栈如下:

libcrypto.1.1.dylib                 0x0000000101d15fa0 int_rsa_verify + 992
libcrypto.1.1.dylib                 0x0000000101d160fb RSA_verify + 139
libcrypto.1.1.dylib                 0x0000000101d12e12 pkey_rsa_verify + 130
libcrypto.1.1.dylib                 0x0000000101cb82f0 EVP_PKEY_verify + 208
libcrypto.1.1.dylib                 0x0000000101cb3490 EVP_DigestVerifyFinal + 464
libcrypto.1.1.dylib                 0x0000000101cb356c EVP_DigestVerify + 156
libcrypto.1.1.dylib                 0x0000000101b89fcf ASN1_item_verify + 959
libcrypto.1.1.dylib                 0x0000000101d7a80e X509_verify + 158
libcrypto.1.1.dylib                 0x0000000101d71ee6 internal_verify + 870
libcrypto.1.1.dylib                 0x0000000101d6fffc verify_chain + 348
libcrypto.1.1.dylib                 0x0000000101d6fad6 X509_verify_cert + 534
libssl.1.1.dylib                    0x00000001016fc1b1 ssl_verify_cert_chain + 705
libssl.1.1.dylib                    0x0000000101730b89 tls_process_server_certificate + 1113
libssl.1.1.dylib                    0x000000010172f771 ossl_statem_client_process_message + 177
libssl.1.1.dylib                    0x000000010172bb55 read_state_machine + 901
libssl.1.1.dylib                    0x000000010172b37a state_machine + 1226
libssl.1.1.dylib                    0x000000010172aea7 ossl_statem_connect + 23
libssl.1.1.dylib                    0x00000001016e6567 ssl3_write_bytes + 503
libssl.1.1.dylib                    0x00000001016f8669 ssl3_write + 105
libssl.1.1.dylib                    0x0000000101709925 ssl_write_internal + 437
libssl.1.1.dylib                    0x000000010170999f SSL_write + 95
openssl                             0x000000010152d38c s_client_main + 24076
openssl                             0x0000000101514a99 do_cmd + 233
openssl                             0x000000010151443b main + 651
dyld                                0x00007ff80337a41f start + 1903     

证书链各级证书的合法性检查

/* verify the issuer signatures and cert times of ctx->chain */
static int internal_verify(X509_STORE_CTX *ctx) {
    X509 *xi, *xs;               // xi是CA证书,xs是待校验证书。
    int n = sk_x509_num(ctx->chain) - 1;    // (3 - 1)
    while (n > 0) {
        fprintf(stderr, "CA: use xi's public key to verify the signature of xs\n");

        if (xs != xi || ((ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)   // 跳过自签名证书,默认不校验
                        && (xi->ex_flags & EXFLAG_SS) != 0)) {
        
            EVP_PKEY *pkey;
            int issuer_depth = n + (xs == xi ? 0 : 1);
            pkey = X509_get0_pubkey(xi);
            X509_verify(xs, pkey);     // 用xi的公钥对xs证书签名进行验签名
        } 
    }
}
  • 证书验签过程依赖DER格式证书,使用ASN.1编码。
    DER证书主要由【header(4) + tbsCertificate + signatureAlgorithm + signature】字段组成,其中tbsCertificate是待签名证书的主体内容,signature是证书签名信息
int int_rsa_verify(int type, const unsigned char *m, unsigned int m_len,
                    unsigned char *rm, size_t *prm_len,
                    const unsigned char *sigbuf, size_t siglen, RSA *rsa) {

    // 用CA的公钥对签名进行解密,解密出的内容为tbsCertificate的sha256哈希值并且进行了ASN.1编码,descrypt_buf = encode_pkcs1(sha256(tbsCertificate))
    decrypt_len = RSA_public_decrypt((int)siglen, sigbuf, decrypt_buf, rsa,
                        RSA_PKCS1_PADDING);

    // 输入的m为证书签名主体tbsCertificate的sha256哈希值,对m进行ASN.1编码
    if (!encode_pkcs1(&encoded, &encoded_len, type, m, m_len))     // 对m(hash)进行ASN.1编码

     
    // 签名验证
    // 比较encode和decrypt_buf ,都是ASN.1编码的sha256(tbsCertificate)
    memcmp(encoded, decrypt_buf, encoded_len) != 0)
}    

3.2 密钥交换

客户端收到服务端发送的 Server Key Exchange 消息后,会提取服务端 EC 公钥,并生成一对本地 EC 公私钥,再将客户端 EC 公钥发送回服务器。双方随后使用各自的私钥和对方的公钥生成共享的对称密钥,用于后续的加密通信。

3.2.1 ECDHE密钥交换过程中对服务端EC公钥进行验签

客户端收到服务端EC公钥后,利用服务端证书中的公钥对服务端EC公钥等信息进行验签,以签名算法rsa_pkcs1_sha512为例

输入:
    cert_pubkey = server_cert_pubkey
    tbs = 【client-random(32) + server-random(32) + Pubkey Length(4) + Pubkey(65)】
    signature = rsa()  
输出:
    hash1 (asn.1) = decrypt_rsa(cert_pubkey, signature)
    hash2 (asn.1) = encode_asn1(sha512 (tbs) )

验证签名:
    memcmp(hash1, hash2)

代码分析如下:

libssl.1.1.dylib                    0x000000010b8fc993 tls_process_ske_ecdhe + 51
libssl.1.1.dylib                    0x000000010b8fa0b2 tls_process_key_exchange + 354
libssl.1.1.dylib                    0x000000010b8f88f0 ossl_statem_client_process_message + 240
libssl.1.1.dylib                    0x000000010b8f4ce5 read_state_machine + 901
libssl.1.1.dylib                    0x000000010b8f450a state_machine + 1226
libssl.1.1.dylib                    0x000000010b8f4037 ossl_statem_connect + 23
libssl.1.1.dylib                    0x000000010b8afce7 ssl3_write_bytes + 503
libssl.1.1.dylib                    0x000000010b8c1899 ssl3_write + 105
libssl.1.1.dylib                    0x000000010b8d2aa5 ssl_write_internal + 437
libssl.1.1.dylib                    0x000000010b8d2b1f SSL_write + 95
openssl                             0x000000010b6fd54c s_client_main + 24076
openssl                             0x000000010b6e4d59 do_cmd + 233
openssl                             0x000000010b6e471b main + 651
dyld                                0x00007ff80337a41f start + 1903

MSG_PROCESS_RETURN tls_process_key_exchange(SSL *s, PACKET *pkt) {
    ...
    EVP_PKEY *pkey = NULL;
    int tls_process_ske_ecdhe(SSL *s, PACKET *pkt, EVP_PKEY **pkey) 
    {
        ...
        if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aECDSA)
            *pkey = X509_get0_pubkey(s->session->peer);            // 获取服务端证书的公钥
        else if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aRSA)     // rsa_pkcs1_sha512 = 1
            *pkey = X509_get0_pubkey(s->session->peer);   
        ...
    }

    if (pkey != NULL) {
         // 构建待验签的内容tbs =  【client-random(32) + server-random(32) + Pubkey Length(4) + Pubkey(65)】
        tbslen = construct_key_exchange_tbs(s, &tbs, PACKET_data(&params), PACKET_remaining(&params));
        // 用服务端证书的公钥对tbs进行验签,
        EVP_DigestVerifyInit(md_ctx, &pctx, md, NULL, pkey);  // md = sha512
        rv = EVP_DigestVerify(md_ctx, PACKET_data(&signature),      
                    PACKET_remaining(&signature), tbs, tbslen);
    }
}

Server Key Exchange数据包

TLSv1.2 Record Layer: Handshake Protocol: Server Key Exchange
    Content Type: Handshake (22)
    Version: TLS 1.2 (0x0303)
    Length: 333
    Handshake Protocol: Server Key Exchange
        Handshake Type: Server Key Exchange (12)
        Length: 329
        EC Diffie-Hellman Server Params
            Curve Type: named_curve (0x03)
            Named Curve: secp256r1 (0x0017)
            Pubkey Length: 65
            Pubkey: 04f0c946215b6087886061603731a85...
            Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
            Signature Length: 256
            Signature [truncated]: 34720afa82341de0b766bfa0...

3.2.2 客户端生成客户端EC公私钥对,把客户端EC公钥发往服务端

int tls_construct_client_key_exchange(SSL *s, WPACKET *pkt) { 
   ...
   tls_construct_cke_ecdhe(SSL *s, WPACKET *pkt) {
    //  服务端EC公钥
    EVP_PKEY* skey = s->s3->peer_tmp;
    //  客户端生成EC公私钥对
    EVP_PKEY* ckey = ssl_generate_pkey(skey);
    /* Generate encoding of client key */
    encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(ckey, &encodedPoint);
    // [encodedPoint, encoded_pt_len]为客户端的EC公钥
    // 客户端的EC公钥通过Client Key Exchange消息发送到服务端
    Handshake Protocol: Client Key Exchange
        Handshake Type: Client Key Exchange (16)
        Length: 66
        EC Diffie-Hellman Client Params
            Pubkey Length: 65
            Pubkey: 041fde8f0f40e54ba73e0d5893bd95a5b13bd02...

       ...
   }    

3.2.2 密钥交换完成后,生成预主密钥

根据客户端的EC私钥和服务单的EC公钥【派生出】预主密钥pre master key

int tls_construct_client_key_exchange(SSL *s, WPACKET *pkt) {
    ...
    int tls_construct_cke_ecdhe(SSL *s, WPACKET *pkt) {
        EVP_PKEY* skey = s->s3->peer_tmp;
        EVP_PKEY* ckey = ssl_generate_pkey(skey);
        // 通过客户端的EC私钥ckey和服务端的EC公钥skey【派生出】预主密钥pre master key
        ssl_derive(s, ckey, skey, 0);
         int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret) {

              EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new(privkey, NULL);
              EVP_PKEY_derive_init(pctx);
              EVP_PKEY_derive_set_peer(pctx, pubkey);
              EVP_PKEY_derive(pctx, NULL, &pmslen);
              EVP_PKEY_derive(pctx, pms, &pmslen);
              
              /* Save premaster secret */
                 s->s3->tmp.pms = pms;
                 s->s3->tmp.pmslen = pmslen;
         }
        
    }
 ...           
}    

4、计算会话密码

  • 根据 ECDHE 生成的 pre-master secret 派生出 master secret。
  • 从 master secret 派生出会话密钥块,并从中提取客户端密钥(client-secret)和服务器密钥(server-secret),分别用于数据加密和签名。
  • 数据发送使用 client-secret,数据接收使用 server-secret,以防止攻击者利用已知的明文和密文推导出密钥流。TLS 协议通过不同的密钥和 IV 来确保每个方向的密钥流独立,从而提高通信安全性。

4.1.1 计算主密钥master secret

s->session->master_key = tls1_PRF(“master key”, client_random, server_random, pre-master-key)

int tls_client_key_exchange_post_work(SSL *s) {
     pms = s->s3->tmp.pms;
     pmslen = s->s3->tmp.pmslen;
     ssl_generate_master_secret(s, pms, pmslen, 1); {
         
         s->method->ssl3_enc->generate_master_secret(s,    // // tls1.2, tls1_generate_master_secret
             s->session->master_key, pms, pmslen,
              &s->session->master_key_length);
}

int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p,
                                size_t len, size_t *secret_size) {

    //......
    tls1_PRF(s,
                TLS_MD_MASTER_SECRET_CONST,
                TLS_MD_MASTER_SECRET_CONST_SIZE,
                s->s3->client_random, SSL3_RANDOM_SIZE,
                NULL, 0,
                s->s3->server_random, SSL3_RANDOM_SIZE,
                NULL, 0, p, len, out,
                SSL3_MASTER_SECRET_SIZE, 1));
    ...

    *secret_size = SSL3_MASTER_SECRET_SIZE;
}

4.1.2 计算key block,从中提取AES128-GCM-SHA256加密加签密钥以及初始向量的前8个字节。

KeyBlock(56) = tls1_PRF(“key expansion”, client_random, server_random, master secret)

代码实现:


// 生成Key Block
static int tls1_generate_key_block(SSL *s, unsigned char *km, size_t num)
{	
    int ret;
     /* Calls SSLfatal() as required */
    ret = tls1_PRF(s,
               TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE, 
               s->s3->server_random, SSL3_RANDOM_SIZE, 
               s->s3->client_random, SSL3_RANDOM_SIZE,
               NULL, 0, NULL, 0, s->session->master_key,
               s->session->master_key_length, km, num, 1);
    return ret;
}

// 从Key Block中提取会话密钥
int tls1_change_cipher_state(SSL *s, int which) {
    // 数据加解密的会话密钥上下文,  数据发送和接收使用不同的加密密钥 
    s->enc_write_ctx;  
    s->enc_read_ctx;

   // 数据签名的会话密钥上下文
   s->write_hash
   s->read_hash
}
  • key block 实例以及生成的密钥和IV 以AES128-GCM为例
key block
61 94 D6 2E 3A 34 83 1D F9 90 4C F4 29 43 F3 B2
E9 E4 49 26 A1 DF 2F ED 6C 6A 2A 98 FB 45 CF 0B
A7 2E E8 4A E8 01 F4 B7 75 0B 91 B1 83 8B AF 2E
0D 32 49 DF 0A 3A 50 28   
  • 数据发送密钥 以及 IV的前4字节
KEY = 61 94 D6 2E 3A 34 83 1D F9 90 4C F4 29 43 F3 B2 
IV(4) = A7 2E E8 4A
  • 数据接收密钥 以及 IV的前4字节
KEY = E9 E4 49 26 A1 DF 2F ED 6C 6A 2A 98 FB 45 CF 0B
IV(4) = E8 01 F4 B7

5. 数据加密加签

为了保证数据的保密性和完整性,在发送数据之前会使用会话密钥对数据加密加签,AES-GCM可支持在加密的同时生成认证标签。

对加密套件ECDHE_RSA_AES128_GCM_SHA256来说,加密加签在aes_gcm_tls_cipher中实现:

1   libcrypto.1.1.dylib                 0x000000010a15c949 aes_gcm_cipher + 73
2   libcrypto.1.1.dylib                 0x000000010a173a2f evp_EncryptDecryptUpdate + 303
3   libcrypto.1.1.dylib                 0x000000010a172d70 EVP_EncryptUpdate + 112
4   libcrypto.1.1.dylib                 0x000000010a172cd3 EVP_CipherUpdate + 67
5   libssl.1.1.dylib                    0x0000000109bb5464 tls13_enc + 1956
6   libssl.1.1.dylib                    0x0000000109baca8c do_ssl3_write + 4924
7   libssl.1.1.dylib                    0x0000000109bab2f0 ssl3_write_bytes + 3408
8   libssl.1.1.dylib                    0x0000000109bbc369 ssl3_write + 105
9   libssl.1.1.dylib                    0x0000000109bcd615 ssl_write_internal + 437
10  libssl.1.1.dylib                    0x0000000109bcd68f SSL_write + 95
11  openssl                             0x00000001099f854c s_client_main + 24076
12  openssl                             0x00000001099dfd59 do_cmd + 233
13  openssl                             0x00000001099df71b main + 651
14  dyld                                0x00007ff80337a41f start + 1903 

static int aes_gcm_tls_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                          const unsigned char *in, size_t len)
{
    EVP_AES_GCM_CTX *gctx = EVP_C_DATA(EVP_AES_GCM_CTX,ctx);
    if (EVP_CIPHER_CTX_ctrl(ctx, ctx->encrypt ? EVP_CTRL_GCM_IV_GEN
                                              : EVP_CTRL_GCM_SET_IV_INV,
                            EVP_GCM_TLS_EXPLICIT_IV_LEN, out) <= 0)
    /* Use saved AAD */ 
    // 输入认证附加数据
    if (CRYPTO_gcm128_aad(&gctx->gcm, ctx->buf, gctx->tls_aad_len))
        goto err;
    in += EVP_GCM_TLS_EXPLICIT_IV_LEN; (8)
    out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
    len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN(16);
    if (ctx->encrypt) {
        /* Encrypt payload  */
        // 进行数据加密
        if (gctx->ctr) {
            size_t bulk = 0;
            if (len >= 32 && AES_GCM_ASM(gctx)) {
                if (CRYPTO_gcm128_encrypt(&gctx->gcm, NULL, NULL, 0))
                    return -1;
                bulk = AES_gcm_encrypt(in, out, len,
                                       gctx->gcm.key,
                                       gctx->gcm.Yi.c, gctx->gcm.Xi.u);
                gctx->gcm.len.u[1] += bulk;
            }
        } 
    }
    out += len;
    /* Finally write tag  */
    // 数据加签,生成认证tag,防止数据被篡改。
    CRYPTO_gcm128_tag(&gctx->gcm, out, EVP_GCM_TLS_TAG_LEN);
}
  • 发送到服务端的加密数据格式为:
   Encrypted Application Data = IV (8) + Encrypted DATA (15) + TAG (16)
  • 第一个数据包: GET / HTTP/1.1 (15)
    附加认证数据AAD:
    AAD: 00 00 00 00 00 00 00 01          sequence
                                17       Content Type: Application Data (23)
                                03 03    Version: TLS 1.2 (0x0303)
                                00 0F    Length:   39 - (IVLen:8 + TagLen:16) = 15
    TAG计算方法 = F(AAD, 密文, IV)
    

    before encryption len = 15
    47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0A  == "GET / HTTP/1.1\n"
    after encryption len = 15
    41 7B CE 29 AF F8 27 6D CF D9 25 F6 43 39 EB  == Encrypted("GET / HTTP/1.1\n")

    Frame 76: 98 bytes on wire (784 bits), 98 bytes captured (784 bits) on interface en0, id 0
    Ethernet II, Src: Apple_5d:ef:60 (14:7d:da:5d:ef:60), Dst: RuijieNetwor_7a:66:d0 (80:05:88:7a:66:d0)
    Internet Protocol Version 4, Src: 192.168.68.124, Dst: 110.242.68.4
    Transmission Control Protocol, Src Port: 57249, Dst Port: 443, Seq: 442, Ack: 5411, Len: 44
    Transport Layer Security
        TLSv1.2 Record Layer: Application Data Protocol: Hypertext Transfer Protocol
            Content Type: Application Data (23)
            Version: TLS 1.2 (0x0303)
            Length: 39

            Encrypted Application Data: 
                e06c9f63 7ea28030(IV) 
                417bce29aff8276dcfd925f64339eb(Encrypted DATA)   
                f9d95ac4f783d6f04e8419ada4577555(TAG)

            [Application Data Protocol: Hypertext Transfer Protocol]
    附加认证数据AAD:
    AAD: 00 00 00 00 00 00 00 02          sequence
                                17       Content Type: Application Data (23)
                                03 03    Version: TLS 1.2 (0x0303)
                                00 14    Length:   36 - (IVLen:8 + TagLen:16) = 20
    
    TAG计算方法 = F(AAD, 密文, IV)

    before encryption len = 20
    48 4F 53 54 3A 20 77 77 77 2E 62 61 69 64 75 2E   == "HOST: www.baidu.com\n"
    63 6F 6D 0A

    after encryption len = 20
    31 7D 37 0B C4 9E 08 F2 5E D6 85 63 90 4E 7E 2D   == Encrypted("HOST: www.baidu.com\n")
    C2 E0 28 11

    Frame 104: 103 bytes on wire (824 bits), 103 bytes captured (824 bits) on interface en0, id 0
    Ethernet II, Src: Apple_5d:ef:60 (14:7d:da:5d:ef:60), Dst: RuijieNetwor_7a:66:d0 (80:05:88:7a:66:d0)
    Internet Protocol Version 4, Src: 192.168.68.124, Dst: 110.242.68.4
    Transmission Control Protocol, Src Port: 57249, Dst Port: 443, Seq: 486, Ack: 5411, Len: 49
    Transport Layer Security
        TLSv1.2 Record Layer: Application Data Protocol: Hypertext Transfer Protocol
            Content Type: Application Data (23)
            Version: TLS 1.2 (0x0303)
            Length: 44

            Encrypted Application Data: 
                e06c9f63 7ea28031(IV)  
                317d370bc49e08f25ed68563904e7e2dc2e02811(Encrypted DATA)   
                d905aa05ad72c24837696fde2131fd1f(TAG)

            [Application Data Protocol: Hypertext Transfer Protocol]

总结

本文通过解析 OpenSSL 源码,详细介绍了 TLS 安全通信协议的实现机制。文章首先简要概述了 TLS 的核心安全功能,包括身份验证、密钥交换、数据加密和数据完整性保护。接着,通过对热门网站加密套件的统计分析,选取了 ECDHE_RSA_AES128_GCM_SHA256 加密套件作为重点解析对象。随后,文章深入剖析了基于该加密套件的 TLS 通信过程,包括数字证书验证、ECDHE 算法密钥交换、AES128-GCM 数据加密以及 SHA256 数据签名。通过这些分析,读者可以更清晰地理解 TLS 协议如何在实际应用中保障通信安全。