
The mTLS handshake is the standard TLS handshake with client authentication switched on: the server presents its certificate and also asks the client for one, and each side proves it owns the private key behind its certificate before any data flows. The result is a connection where both ends are cryptographically identified, not just encrypted. This walkthrough goes message by message through that flow, focusing on the current version, TLS 1.3, defined in RFC 8446.
For the concepts behind it, start with What Is mTLS?. This post is the mechanics.
In TLS 1.3, the whole exchange fits in one round trip, and mutual authentication adds the client-certificate messages to it. Step by step:
**ClientHello.** The client opens with the TLS versions and cipher suites it supports and its key-share material for the key exchange.
**ServerHello.** The server selects the parameters and sends its own key share. From this point on, the rest of the handshake is encrypted.
**CertificateRequest.** Because this is mTLS, the server asks the client to authenticate with a certificate. In TLS 1.3 this message is sent before the server's own certificate, as part of the server's flight.
**Server Certificate and CertificateVerify.** The server sends its certificate chain, then a CertificateVerify, a signature over the handshake transcript made with its private key, which proves it actually holds the key in that certificate. It closes its flight with a Finished message.
**Client Certificate and CertificateVerify.** The client sends its certificate chain and its own CertificateVerify, signing the transcript with its private key to prove possession, then sends Finished.
**Both sides validate, and the channel opens.** Each side verifies the other's certificate, and once both check out, the session keys agreed during the exchange protect all traffic that follows.
The one idea to hold onto: identity here is settled during connection setup, by cryptography, before the first request is served.
CertificateVerify is what turns "I have a certificate" into "I own this certificate." A certificate is public, anyone who intercepts one can copy it, so presenting a certificate alone proves nothing. In CertificateVerify, the sender signs the running hash of the handshake with the private key that matches its certificate. The other side checks that signature against the public key in the certificate.
Because the private key never leaves its owner and the signature covers this specific handshake, a copied certificate is useless to an attacker: without the private key they cannot produce a valid CertificateVerify, and the signature cannot be replayed onto a different handshake. This is the step that makes mTLS resistant to certificate theft and replay.
Verifying the signature is necessary but not sufficient. A complete check also confirms that the certificate chains to a certificate authority in the verifier's truststore, that it is within its validity dates, that it has not been revoked, and that its key usage permits what it is being used for. A perfectly signed certificate from an untrusted or expired CA should still be rejected.
Revocation is the part teams most often get wrong. A certificate can be valid today and compromised tomorrow, so verifiers check revocation status through certificate revocation lists, defined in RFC 5280, or the Online Certificate Status Protocol, RFC 6960. The trade-off is freshness versus latency: checking status live is the most current but adds a round trip, while cached or list-based checks are faster but can lag a revocation.
The messages are similar, the choreography is not. TLS 1.2 takes two round trips and orders things differently, the server sends its certificate first and the CertificateRequest afterward, and the client's certificate and CertificateVerify come in a later flight. TLS 1.3 compresses the exchange to a single round trip, encrypts most of the handshake, and moves the CertificateRequest ahead of the server's certificate. The security roles are the same in both, both sides present certificates and prove possession, TLS 1.3 just does it faster and with more of the handshake hidden.
Once both certificates are verified, the two sides share session keys and every message after that is encrypted and authenticated. Nothing downstream has to re-ask who is calling, because the connection itself already carries proven identities.
That verified client identity is also what OAuth builds on for certificate-bound access tokens. Under RFC 8705, the same client certificate used in the handshake is bound to the access token the authorization server issues, so a stolen token cannot be replayed from a client that lacks the matching certificate.
MonoCloud acts as the mTLS endpoint for your machine, workload, and agent traffic. A client opens an mTLS connection and authenticates with its certificate, and MonoCloud validates the chain against your truststore, using AWS Private CA, Google Cloud, HashiCorp Vault, a SPIRE trust bundle for SPIFFE workloads, or your own online or offline CA, and checks revocation live through CRLs and OCSP with local deny lists as a fallback. Validation is tunable per truststore, covering expiry, key-purpose checks, revocation depth, cache windows, and clock-skew tolerance. On success it issues a certificate-bound access token (RFC 8705), and because access policy is evaluated at issuance, whether a valid client certificate was presented can itself gate the token, as covered in What Are Cedar Policies?. See the MonoCloud mTLS page, or start building for free.
What is the mTLS handshake?
The mTLS handshake is the standard TLS handshake with client authentication enabled. The server presents its certificate and also requests one from the client, and each side signs the handshake with its private key to prove it owns its certificate. When both verify, a shared encrypted session opens with both ends cryptographically identified.
How is the mTLS handshake different from a normal TLS handshake?
A normal TLS handshake authenticates only the server; the client stays anonymous at the connection layer. The mTLS handshake adds a CertificateRequest from the server and a Certificate plus CertificateVerify from the client, so the client is authenticated too. It is the same protocol, just with the optional client-authentication steps turned on.
What is the CertificateVerify step and why does it matter?
CertificateVerify is a signature over the handshake transcript made with the sender's private key. It proves the sender actually owns the certificate it presented, rather than having copied someone else's. Because a certificate is public, this proof of possession is what makes the handshake resistant to certificate theft and replay.
How does mTLS prevent certificate theft or replay?
The private key never leaves its owner, and the CertificateVerify signature covers the specific handshake in progress. An attacker with a copied certificate cannot produce a valid signature without the private key, and a signature from one handshake cannot be reused on another, so a stolen certificate alone is useless.
How many round trips does the mTLS handshake take?
In TLS 1.3 the handshake completes in a single round trip, with most of it encrypted and the CertificateRequest sent before the server's certificate. TLS 1.2 takes two round trips and orders the messages differently. The security roles are identical in both versions.