Understanding TLS Handshake: Finished Message, Session Tickets, and Alerts

This article explains the TLS handshake process, detailing the Finished message verification, the use of NewSessionTicket for stateless session resumption, and the roles of ChangeCipherSpec, Alert, and Application Data protocols in secure communications.

WeChat Backend Team
WeChat Backend Team
WeChat Backend Team
Understanding TLS Handshake: Finished Message, Session Tickets, and Alerts

5.11. handshake — Finished

After the ChangeCipherSpec message, the Finished message must be sent immediately to confirm that the key exchange and authentication have succeeded. ChangeCipherSpec must appear between other handshake messages and the Finished message.

The Finished message is the first one protected by the newly negotiated parameters. The receiver must verify the correctness of the Finished message. Once both sides have exchanged and verified Finished messages, application data can be sent.

Message structure:

struct {
    opaque verify_data[verify_data_length];
} Finished;

verify_data = PRF(master_secret, finished_label, Hash(handshake_messages))
            [0..verify_data_length-1];

finished_label = "client finished" for client messages, "server finished" for server messages.

Hash denotes the hash of all handshake messages; the hash function is the one used by the PRF or the one defined by the CipherSuite.

In earlier TLS versions, verify_data is always 12 bytes. In TLS 1.2 its length depends on the CipherSuite; if unspecified, it defaults to 12 bytes. Future CipherSuites may define other lengths, but never less than 12 bytes.

The Finished message must follow the ChangeCipherSpec message; any reordering results in a fatal error.

The handshake_message content includes all messages from ClientHello up to, but not including, the current Finished message, containing only handshake layer payloads (excluding record layer headers). It also includes CertificateVerify. The content differs for client and server, and the later sender must include the earlier sender's Finished message.

Note: ChangeCipherSpec, alert, and other record types are not handshake messages and are excluded from the hash calculation, as is HelloRequest.

5.12. handshake — NewSessionTicket

SessionTicket is defined in RFC 5077 (2008) and provides a stateless way to resume TLS sessions without server-side state. It works with any CipherSuite and is applicable to TLS 1.0, 1.1, and 1.2.

It is especially useful when handling a large number of users, when server memory is limited, or when multiple servers need to share no state.

Clients indicate support by including a SessionTicket extension in ClientHello. If the client has no stored ticket, the extension is empty.

When a server uses SessionTicket, it encrypts the session state into a ticket, protects it with a MAC, and sends it to the client in a NewSessionTicket message, which must be sent before ChangeCipherSpec and after the server has verified the client’s Finished message.

Client                                 Server
ClientHello
(empty SessionTicket extension) ------>
                                      ServerHello
                                      (empty SessionTicket extension)
                                      Certificate*
                                      ServerKeyExchange*
                                      CertificateRequest*
                                      ServerHelloDone <------
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------------------------->
                                      NewSessionTicket
                                      [ChangeCipherSpec]
                                      <------ Finished
Application Data <--------------------> Application Data

Figure 1: Message flow for full handshake issuing new session ticket

The client stores the received ticket together with the master secret and other session parameters. To resume a session, the client includes the ticket in the SessionTicket extension of a new ClientHello.

The server decrypts the ticket, verifies its MAC, and restores the session state. If verification succeeds, the server may send a new NewSessionTicket after ServerHello.

This abbreviated handshake saves one RTT compared to a full handshake.

Client                                 Server
ClientHello (SessionTicket extension) ------>
                                      ServerHello (empty SessionTicket extension)
                                      NewSessionTicket
                                      [ChangeCipherSpec]
                                      <------ Finished
[ChangeCipherSpec]
Finished -------------------------->
Application Data <--------------------> Application Data

Figure 2: Message flow for abbreviated handshake using new session ticket

If the server cannot or does not wish to use the client’s ticket, it can ignore the ticket and perform a full handshake.

If the server does not want to issue a new ticket, it may omit the SessionTicket extension or send a NewSessionTicket message of zero length.

When the server rejects a received ticket, it may still issue a new ticket after completing a full handshake.

NewSessionTicket message format:

struct {
    uint32 ticket_lifetime_hint;
    opaque ticket<0..2^16-1>;
} NewSessionTicket;

The ticket_lifetime_hint field indicates how long (in seconds) the ticket is considered valid.

struct {
    opaque key_name[16];
    opaque iv[16];
    opaque encrypted_state<0..2^16-1>;
    opaque mac[32];
} ticket;

RFC 5077 recommends encrypting the ticket with AES‑128‑CBC and protecting it with HMAC‑SHA‑256 (Encrypt‑then‑MAC). The key_name identifies the key set used, allowing key rotation.

In OpenSSL, a session is serialized in ASN.1 as:

typedef struct ssl_session_asn1_st {
    ASN1_INTEGER version;
    ASN1_INTEGER ssl_version;
    ASN1_OCTET_STRING cipher;
    ASN1_OCTET_STRING master_key;
    ASN1_OCTET_STRING session_id;
    ASN1_OCTET_STRING session_id_context;
    ASN1_INTEGER time;
    ASN1_INTEGER timeout;
    ASN1_INTEGER verify_result;
    ASN1_OCTET_STRING tlsext_hostname;
    ASN1_INTEGER tlsext_tick_lifetime;
    ASN1_OCTET_STRING tlsext_tick;
} SSL_SESSION_ASN1;

6. ChangeCipherSpec Protocol

ChangeCipherSpec notifies the peer to start using the negotiated Connection State for symmetric encryption; it contains only a single byte. This protocol is redundant and was removed in TLS 1.3.

Example capture:

7. Alert Protocol

The Alert protocol provides a simple return‑code mechanism.

enum { warning(1), fatal(2), (255) } AlertLevel;

struct {
    AlertLevel level;
    AlertDescription description;
} Alert;

The level determines how the alert is handled. The "close_notify" alert signals that no more data will be sent, allowing the peer to close the connection proactively and reduce TCP TIME_WAIT sockets.

Alert protocol diagram:

8. Application Data Protocol

Application Data records carry raw application payloads, which are segmented, MACed, encrypted, and transmitted.

Example capture:

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TLScryptographyHandshakeSessionTicket
WeChat Backend Team
Written by

WeChat Backend Team

Official account of the WeChat backend development team, sharing their experience in large-scale distributed system development.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.