TLS 1.3 deep dive – handshake, ciphers, and mutual TLS – Secured Me

TLS 1.3 deep dive – handshake, ciphers, and mutual TLS

How the TLS 1.3 handshake actually works, why it is faster and safer than 1.2, and how to deploy modern TLS (and mTLS) correctly.

TLS quietly underpins almost everything: HTTPS, email transport, VPNs, database connections, service-to-service traffic in modern microservices. TLS 1.3 (RFC 8446, finalised in 2018) is the version you should be running everywhere by now — it is faster, dramatically simpler, and removes a long list of historically dangerous options. Understanding what the handshake actually does, what changed from 1.2, and how mutual TLS (mTLS) fits in is genuinely useful whether you are deploying a web server, debugging a broken integration, or designing a zero-trust network.

Why TLS 1.3 matters

TLS 1.2 accumulated nearly a decade of footguns: RSA key exchange (no forward secrecy), CBC ciphers vulnerable to padding oracles, RC4, SHA-1 in certificates, compression (CRIME), renegotiation issues, and dozens of cipher suite permutations to misconfigure. Attacks like BEAST, CRIME, BREACH, Lucky13, POODLE, FREAK, Logjam, and ROBOT all exploited corners of TLS 1.2 or earlier. TLS 1.3 was a deliberate clean-sheet design: drop everything dangerous, mandate forward secrecy, modernise the handshake, and make misconfiguration much harder. The cipher suite list shrunk from dozens to just five AEAD options. Most browsers, load balancers, and modern libraries now default to TLS 1.3 when both sides support it.

The TLS 1.3 handshake, step by step

A standard handshake takes one round trip (1-RTT) before encrypted application data flows. The basic flow:

  • ClientHello – The client sends supported TLS versions, a list of cipher suites, supported elliptic curves (supported_groups), signature algorithms, and a key share: one or more ephemeral public keys for the curves it expects the server to choose (typically X25519 or P-256). It also sends extensions like SNI (the hostname) and ALPN (the application protocol).
  • ServerHello – The server picks a cipher suite and a curve, sends its own ephemeral public key share, and from that point on encrypts the rest of the handshake using a key derived from the shared ECDHE secret. This is a big change from TLS 1.2, where certificates were sent in clear.
  • EncryptedExtensions, Certificate, CertificateVerify, Finished – All encrypted. The server sends its certificate chain, signs the handshake transcript with its private key (proving it actually holds the key), and finishes.
  • Client Finished – The client verifies the chain, checks the signature, and sends its own Finished message. Application data can now flow in both directions.
  • A few important properties fall out of this design:

    Cipher suites and key exchange

    TLS 1.3 defines just five cipher suites, all AEAD (Authenticated Encryption with Associated Data):

    Key exchange is always (EC)DHE — RSA key transport is gone. Sensible curves are X25519 (fast, constant-time, widely supported) and P-256. Signature algorithms used by certificates have also been pruned: ECDSA with P-256/P-384 or RSA-PSS are the modern defaults; SHA-1 is dead.

    A reasonable modern policy for a public HTTPS service: TLS 1.3 enabled, TLS 1.2 enabled only with AEAD suites and ECDHE for backwards compatibility, everything older disabled. Test with testssl.sh, SSL Labs, or nmap --script ssl-enum-ciphers.

    Mutual TLS (mTLS)

    Standard TLS authenticates the server to the client. mTLS authenticates the client to the server as well, using a client certificate. It is the workhorse of zero-trust service-to-service authentication in modern infrastructure: service meshes (Istio, Linkerd), VPN replacements (BeyondCorp-style), IoT device authentication, and high-assurance B2B APIs.

    The mechanics are an extension of the normal handshake: the server sends a CertificateRequest, the client responds with its certificate and a CertificateVerify signed with its private key, proving possession. Both sides validate the other's chain against a trusted CA.

    Practical considerations for running mTLS at scale:

    Operational pitfalls

    A few patterns cause most of the TLS-related incidents in practice. Certificate expiry is still the largest self-inflicted outage source; automate renewal (ACME / Let's Encrypt for public, internal automation for private) and alert at multiple thresholds (60/30/7 days). Mismatched protocol or cipher requirements between client and server cause silent fallback to older versions when not pinned; modern services should refuse anything below TLS 1.2. SNI is sent in plaintext by default — if you need to hide the hostname, look at Encrypted Client Hello (ECH), now supported by Cloudflare and recent browsers. TLS termination at a load balancer is fine for the public edge, but treat internal hops as untrusted: use TLS or mTLS end-to-end where the data warrants it, not just at the perimeter. Finally, key material lives somewhere; protect private keys with HSMs or cloud KMS for high-value certificates, restrict file permissions on the rest, and never commit them to source control. TLS is one of the few areas in security where defaults have genuinely got better — make sure your stack actually uses them.