本篇主要介绍TLS 1.3在 OpenSSL 中的实现,加密套件选取TLS_AES_256_GCM_SHA384

1. 加密套件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 ------> | 
  |                                 |

2. 密钥交换

TLS 1.3安全协议中默认采用ECDHE密钥交换算法,椭圆曲线选取x25519

2.1 客户端生成EC公私钥对,发送EC公钥到服务端

在客户端生成EC公私钥对,通过ClientHello消息中的key share携带EC公钥到服务端,生成密钥代码以及调用栈如下:

1   libssl.1.1.dylib                    0x00000001089f8ec6 ssl_generate_pkey_group + 54
2   libssl.1.1.dylib                    0x0000000108a1f466 add_key_share + 214
3   libssl.1.1.dylib                    0x0000000108a1f30b tls_construct_ctos_key_share + 427
4   libssl.1.1.dylib                    0x0000000108a1b040 tls_construct_extensions + 496
5   libssl.1.1.dylib                    0x0000000108a2eb9f tls_construct_client_hello + 1967
6   libssl.1.1.dylib                    0x0000000108a2c039 write_state_machine + 1049
7   libssl.1.1.dylib                    0x0000000108a2b1cf state_machine + 1295
8   libssl.1.1.dylib                    0x0000000108a2acb7 ossl_statem_connect + 23
9   libssl.1.1.dylib                    0x00000001089e6827 ssl3_write_bytes + 503
10  libssl.1.1.dylib                    0x00000001089f83d9 ssl3_write + 105
11  libssl.1.1.dylib                    0x0000000108a09685 ssl_write_internal + 437
12  libssl.1.1.dylib                    0x0000000108a096ff SSL_write + 95
13  openssl                             0x000000010883454c s_client_main + 24076
14  openssl                             0x000000010881bd59 do_cmd + 233
15  openssl                             0x000000010881b71b main + 651
16  dyld                                0x00007ff80337a41f start + 1903
chunbo client generated public key
47 A8 B0 6A 47 51 5A FA F4 4E 17 7E A4 0A E7 0E 
17 D5 AD EC D4 21 04 53 60 B7 93 3A 50 18 BF 5E 

static int add_key_share(SSL *s, WPACKET *pkt, unsigned int curve_id) {
    EVP_PKEY *key_share_key = NULL;
    key_share_key = ssl_generate_pkey_group(s, curve_id);
    /* Encode the public key. */
    encodedlen = EVP_PKEY_get1_tls_encodedpoint(key_share_key,
                                                &encoded_point);
    ...
}

发送的ClientHello消息实例:

Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 312
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 308
            Version: TLS 1.2 (0x0303)
            Random: f91d878cc5c039b6d309d0619d033785a760c2436ec844cbc403730c28363362
            Session ID Length: 32
            Session ID: eb68913f606d2aec3fd6e433338464b9d94cef88d904586b5473642c070090d1
            Cipher Suites Length: 62
            Cipher Suites (31 suites)
                Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
                ...
            Compression Methods Length: 1
            Compression Methods (1 method)
            Extensions Length: 173
            Extension: server_name (len=20) name=www.sina.com.cn
                Type: server_name (0)
                Length: 20
                Server Name Indication extension
            ...
            Extension: key_share (len=38) x25519
                Type: key_share (51)
                Length: 38
                Key Share extension
                    Client Key Share Length: 36
                    Key Share Entry: Group: x25519, Key Exchange length: 32
                        Group: x25519 (29)
                        Key Exchange Length: 32
                        Key Exchange: d4216c1bc006e77828791b89a60466e62d504101cf19325d375d1c54499e4b0e

2.2 接收服务端的EC公钥,生成预主密钥Pre Master Secret

接收到的服务端ServerHello消息,扩展属性会携带服务端的EC公钥,根据服务端的EC公钥和客户端的EC私钥生成预主密钥Pre Master Secret

  • 服务端ServerHello实例 :
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Server Hello
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 122
        Handshake Protocol: Server Hello
            Handshake Type: Server Hello (2)
            Length: 118
            Version: TLS 1.2 (0x0303)
            Random: 6ef03ada63c4c3d6e5b49758cdf41a3fa54136ed6a819d9d0454298e6daf8af4
            Session ID Length: 32
            Session ID: eb68913f606d2aec3fd6e433338464b9d94cef88d904586b5473642c070090d1
            Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
            Compression Method: null (0)
            Extensions Length: 46
            Extension: supported_versions (len=2) TLS 1.3
                Type: supported_versions (43)
                Length: 2
                Supported Version: TLS 1.3 (0x0304)
            Extension: key_share (len=36) x25519
                Type: key_share (51)
                Length: 36
                Key Share extension
                    Key Share Entry: Group: x25519, Key Exchange length: 32
                        Group: x25519 (29)
                        Key Exchange Length: 32
                        Key Exchange: 9afba7217dce9e28b6748339ec0d7273021252ae06c7ce9538e60b557d48fc7e
  • 根据预主密钥Pre-Master-Secret派生出握手密钥Handshake Secret
1   libssl.1.1.dylib                    0x0000000108a52e17 tls13_generate_secret + 119
2   libssl.1.1.dylib                    0x00000001089f9418 ssl_derive + 536
3   libssl.1.1.dylib                    0x0000000108a22b1e tls_parse_stoc_key_share + 1182
4   libssl.1.1.dylib                    0x0000000108a1abca tls_parse_extension + 282
5   libssl.1.1.dylib                    0x0000000108a1acb5 tls_parse_all_extensions + 117
6   libssl.1.1.dylib                    0x0000000108a30264 tls_process_server_hello + 2948
7   libssl.1.1.dylib                    0x0000000108a2f507 ossl_statem_client_process_message + 135
8   libssl.1.1.dylib                    0x0000000108a2b965 read_state_machine + 901
9   libssl.1.1.dylib                    0x0000000108a2b18a state_machine + 1226
10  libssl.1.1.dylib                    0x0000000108a2acb7 ossl_statem_connect + 23
11  libssl.1.1.dylib                    0x00000001089e6827 ssl3_write_bytes + 503
12  libssl.1.1.dylib                    0x00000001089f83d9 ssl3_write + 105
13  libssl.1.1.dylib                    0x0000000108a09685 ssl_write_internal + 437
14  libssl.1.1.dylib                    0x0000000108a096ff SSL_write + 95
15  openssl                             0x000000010883454c s_client_main + 24076
16  openssl                             0x000000010881bd59 do_cmd + 233
17  openssl                             0x000000010881b71b main + 651
18  dyld                                0x00007ff80337a41f start + 1903

int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int gensecret) {

    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);

    // 根据本地EC私钥privkey和服务端EC公钥pubkey生成预主密钥Pre-Master-Secret
    EVP_PKEY_derive(pctx, pms, &pmslen);

    if (gensecret) {

        // 生成ealy_secret
        tls13_generate_secret(s, ssl_handshake_md(s), NULL, NULL, 0, (unsigned char *)&s->early_secret);


        // 使用HKDF算法, 通过Pre-Master-Secret派生出握手密钥用于握手消息加密
        tls13_generate_handshake_secret(s, pms, pmslen); {
            return tls13_generate_secret(s, ssl_handshake_md(s), s->early_secret,
                                        insecret, insecretlen,
                                        (unsigned char *)&s->handshake_secret);            
        }
    }


}

/*
 * Given the previous secret |prevsecret| and a new input secret |insecret| of
 * length |insecretlen|, generate a new secret and store it in the location
 * pointed to by |outsecret|. Returns 1 on success  0 on failure.
 */
int tls13_generate_secret(SSL *s, const EVP_MD *md,
                          const unsigned char *prevsecret,
                          const unsigned char *insecret,
                          size_t insecretlen,
                          unsigned char *outsecret)
{
        /* Generate the pre-extract secret */
        if (!tls13_hkdf_expand(s, md, prevsecret,
                               (unsigned char *)derived_secret_label,
                               sizeof(derived_secret_label) - 1, hash, mdlen,
                               preextractsec, mdlen, 1));
        
        EVP_PKEY_derive_init(pctx);
        EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
        EVP_PKEY_CTX_set_hkdf_md(pctx, md);
        EVP_PKEY_CTX_set1_hkdf_key(pctx, insecret, insecretlen);
        EVP_PKEY_CTX_set1_hkdf_salt(pctx, prevsecret, prevsecretlen);
        EVP_PKEY_derive(pctx, outsecret, &mdlen);
}

3. 生成握手密钥,继续握手

在握手完成后,会接着发送四条加密的握手消息,分别是Server EncryptedExtensions消息, Server Certificate消息, Server CertificateVerify消息 , Server Finished消息, 如下:

     |<------ 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
  • Server Certificate 主要是对服务端身份进行校验,校验过程同TLS 1.2中的身份认证,不再赘述。

4. 握手完成派生出应用数据加密密钥

4.1 TLS 1.3密钥派生关系

完成握手后,会继续生成应用数据加密密钥,派生关系是: handshake secret -> master secret -> app_traffic_secret

详细的密钥派生关系参见: https://datatracker.ietf.org/doc/html/rfc8446#section-10

           0
             |
             v
   PSK ->  HKDF-Extract = Early Secret
             |
             +-----> Derive-Secret(., "ext binder" | "res binder", "")
             |                     = binder_key
             |
             +-----> Derive-Secret(., "c e traffic", ClientHello)
             |                     = client_early_traffic_secret
             |
             +-----> Derive-Secret(., "e exp master", ClientHello)
             |                     = early_exporter_master_secret
             v
       Derive-Secret(., "derived", "")
             |
             v
   (EC)DHE -> HKDF-Extract = Handshake Secret
             |
             +-----> Derive-Secret(., "c hs traffic",
             |                     ClientHello...ServerHello)
             |                     = client_handshake_traffic_secret
             |
             +-----> Derive-Secret(., "s hs traffic",
             |                     ClientHello...ServerHello)
             |                     = server_handshake_traffic_secret
             v
       Derive-Secret(., "derived", "")
             |
             v
   0 -> HKDF-Extract = Master Secret
             |
             +-----> Derive-Secret(., "c ap traffic",
             |                     ClientHello...server Finished)
             |                     = client_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "s ap traffic",
             |                     ClientHello...server Finished)
             |                     = server_application_traffic_secret_0
             |
             +-----> Derive-Secret(., "exp master",
             |                     ClientHello...server Finished)
             |                     = exporter_master_secret
             |
             +-----> Derive-Secret(., "res master",
                                   ClientHello...client Finished)
                                   = resumption_master_secret

4.2 根据handshake secret派生master-secret

生成master secret的代码以及调用关系如下:

1   libssl.1.1.dylib                    0x000000010bfd3da7 tls13_generate_secret + 119
2   libssl.1.1.dylib                    0x000000010bfd43e8 tls13_generate_master_secret + 120
3   libssl.1.1.dylib                    0x000000010bfbcc76 tls_process_finished + 1190
4   libssl.1.1.dylib                    0x000000010bfb0569 ossl_statem_client_process_message + 345
5   libssl.1.1.dylib                    0x000000010bfac8f5 read_state_machine + 901
6   libssl.1.1.dylib                    0x000000010bfac11a state_machine + 1226
7   libssl.1.1.dylib                    0x000000010bfabc47 ossl_statem_connect + 23
8   libssl.1.1.dylib                    0x000000010bf67797 ssl3_write_bytes + 503
9   libssl.1.1.dylib                    0x000000010bf79369 ssl3_write + 105
10  libssl.1.1.dylib                    0x000000010bf8a615 ssl_write_internal + 437
11  libssl.1.1.dylib                    0x000000010bf8a68f SSL_write + 95
12  openssl                             0x000000010bdb554c s_client_main + 24076
13  openssl                             0x000000010bd9cd59 do_cmd + 233
14  openssl                             0x000000010bd9c71b main + 651
15  dyld                                0x00007ff80337a41f start + 1903


MSG_PROCESS_RETURN tls_process_finished(SSL *s, PACKET *pkt) {
    ...
    if (SSL_IS_TLS13(s)) {
        if (s->server) {
            ...
        } else {
            /* TLS 1.3 gets the secret size from the handshake md */
            size_t dummy;
            // 根据握手密钥派生出主密钥
            if (!s->method->ssl3_enc->generate_master_secret(s,
                    s->master_secret, s->handshake_secret, 0,
                    &dummy)) {
            }
            if (!s->method->ssl3_enc->change_cipher_state(s,
                    SSL3_CC_APPLICATION | SSL3_CHANGE_CIPHER_CLIENT_READ)) {
            }
        }
    }
}

4.3 主密钥master secret派生出应用数据加密加签密钥;

最终派生出client_app_traffic_secret, server_app_traffic_secret分别用于客户端和服务端数据加密

int tls13_change_cipher_state(SSL *s, int which) {

    static const unsigned char client_early_traffic[] = "c e traffic";
    static const unsigned char client_handshake_traffic[] = "c hs traffic";
    static const unsigned char client_application_traffic[] = "c ap traffic";
    static const unsigned char server_handshake_traffic[] = "s hs traffic";
    static const unsigned char server_application_traffic[] = "s ap traffic";
    static const unsigned char exporter_master_secret[] = "exp master";
    static const unsigned char resumption_master_secret[] = "res master";
    static const unsigned char early_exporter_master_secret[] = "e exp master";

    unsigned char *iv;
    unsigned char secret[EVP_MAX_MD_SIZE];
    unsigned char hashval[EVP_MAX_MD_SIZE];
    unsigned char *hash = hashval;
    unsigned char *insecret;
    unsigned char *finsecret = NULL;
    const unsigned char *label;
    if (which & SSL3_CC_READ) {

    }

    if (((which & SSL3_CC_CLIENT) && (which & SSL3_CC_WRITE)) {
        if (which & SSL3_CC_HANDSHAKE) {
            insecret = s->handshake_secret;
            label = client_handshake_traffic;

        } else {
            insecret = s->master_secret;
            label = client_application_traffic;
        }
    } else {
        if (which & SSL3_CC_HANDSHAKE) {
            insecret = s->handshake_secret;
            label = server_handshake_traffic;
        } else {
            insecret = s->master_secret;
            label = server_application_traffic;
        }
    }

    // 派生出密钥
    if (!derive_secret_key_and_iv(s, which & SSL3_CC_WRITE, md, cipher,
                                  insecret, hash, label, labellen, secret, iv,
                                  ciph_ctx)) {
        /* SSLfatal() already called */
        goto err;
    }


    // 服务端加密加签密钥
    if (label == server_application_traffic) {
        memcpy(s->server_app_traffic_secret, secret, hashlen);
    } else if (label == client_application_traffic)
    // 客户端加密加签密钥
        memcpy(s->client_app_traffic_secret, secret, hashlen);
    

4.3 使用应用数据加密密钥;

加密调用代码以及调用栈如下, AES_GCM加密加签代码与TLS 1.2加密套件ECDHE_RSA_AES128_GCM_SHA256类似:

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_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
                          const unsigned char *in, size_t len)
{
    // 输入认证附加数据 
    if (gctx->tls_aad_len >= 0)
        return aes_gcm_tls_cipher(ctx, out, in, len);

    if (in) {
        if (ctx->encrypt) {
            if (gctx->ctr) {
                size_t bulk = 0;
                //  AES_GCM加密
                if (CRYPTO_gcm128_encrypt_ctr32(&gctx->gcm,
                                                in + bulk,
                                                out + bulk,
                                                len - bulk, gctx->ctr))
                    return -1;
            } 
        } else {
            if (gctx->ctr) {
                size_t bulk = 0;
                // AES_GCM解密
                if (CRYPTO_gcm128_decrypt_ctr32(&gctx->gcm,
                                                in + bulk,
                                                out + bulk,
                                                len - bulk, gctx->ctr))
                    return -1;
            } 
        }
        return len;
    } else {
        //数据加签,生成认证tag,防止数据被篡改。 
        CRYPTO_gcm128_tag(&gctx->gcm, ctx->buf, 16);
        gctx->taglen = 16;
        /* Don't reuse the IV */
        gctx->iv_set = 0;
        return 0;
    }

}