Which TLS 1.3 message asks a client to authenticate, and what does the client's CertificateVerify sign?
answer
- the server has to ask first
- one message, several extensions
- required scheme list, optional issuer hint
- the certificate alone proves nothing
- signature covers the handshake transcript
basics
~20 sThe server sends CertificateRequest, after EncryptedExtensions and before its own Certificate. The client answers with Certificate, then CertificateVerify, whose signature covers a hash of the handshake transcript so far, made with the private key for the leaf it just sent.
solid answer
~40 sA TLS 1.3 server asks by placing `CertificateRequest` in its flight, after `EncryptedExtensions` and before its own `Certificate`. The message carries a context value plus extensions: `signature_algorithms(13)`, which is required and constrains what the client may sign with; optionally `certificate_authorities(47)`, a hint naming issuers the server will accept so the client can pick sensibly; and optionally `oid_filters(48)`, asking that the presented certificate carry particular extension values. The client replies with `Certificate`, then `CertificateVerify`, then `Finished`. The `CertificateVerify` signature is over a hash of the handshake transcript up to that point, together with a context string that differs for client and server signatures. That proves possession of the private key for the leaf just sent and binds the proof to this handshake, so it cannot be replayed elsewhere.
code
pseudocode · 15 lines// TLS 1.3 server flight, asking for client authentication
CertificateRequest {
certificate_request_context: (empty) // non-empty only post-handshake
extensions:
signature_algorithms(13): [ecdsa_secp256r1_sha256, rsa_pss_rsae_sha256]
certificate_authorities(47): [issuer names the server accepts] // a hint
oid_filters(48): [certificate extension values wanted]
}
// client flight in reply
Certificate { certificate_request_context: (echoed), certificate_list: [leaf, intermediates] }
CertificateVerify { algorithm: ecdsa_secp256r1_sha256,
signature: sign(client_private_key,
context_string + transcript_hash) }
Finishedgo deeper
Remember the shape: the server asks with CertificateRequest, the client answers with a certificate plus a signature. Nothing about a client is sent unless the request arrived first.
Be able to name what the request carries and why the signature matters: the required scheme list, the optional issuer hint, and a CertificateVerify over the handshake transcript that proves possession of the private key.
Use the structure diagnostically. Whether a client certificate reached the server at all separates a selection problem from a validation problem, and the two have completely different fixes.
Consider what the constraints cost across an estate. A narrow scheme list or a long issuer hint shapes which fleets can connect at all, so tightening either is a compatibility decision, not just a hardening one.
## Where the request sits in the flight In TLS 1.3 the server's flight is fixed: `ServerHello`, then `EncryptedExtensions`, then, when client authentication is wanted, `CertificateRequest`, then the server's own `Certificate` and `CertificateVerify`, then `Finished`. Everything from `EncryptedExtensions` onward is already protected, so the demand for a client certificate is not visible to a passive observer. A server that does not want client authentication simply omits the message. There is no negotiation and no flag elsewhere: the presence of `CertificateRequest` **is** the request. ## What the request carries The message body is a context value plus a set of extensions. - **`certificate_request_context`** - an opaque value. It is empty in the main handshake and non-empty for a post-handshake request, and the client echoes it back in its `Certificate` message so an answer can be matched to its request. - **`signature_algorithms(13)`** - required in a `CertificateRequest`. It lists the signature schemes the server will accept in the client's `CertificateVerify`, which in practice constrains what kind of key the client's certificate may hold. - **`certificate_authorities(47)`** - optional. It names issuers the server is willing to accept, as a hint the client should use when choosing among several certificates it holds. It is guidance for selection, not the server's acceptance check. - **`oid_filters(48)`** - optional, and only ever appears here. It asks that the certificate the client selects carry particular certificate extension values, which is how a server narrows selection beyond the issuer. - **`signature_algorithms_cert(50)`** - optional, separating the schemes acceptable in the certificate chain's own signatures from those acceptable in the `CertificateVerify`. ## What the client sends back 1. **`Certificate`** - the client's end-entity leaf, plus whatever intermediates the server may need, with the request context echoed. 2. **`CertificateVerify`** - a signature, using the private key for that leaf, over a hash of the handshake transcript so far, combined with a context string that distinguishes a client signature from a server one. 3. **`Finished`** - which closes the client's flight as it always does. The ordering matters for a reason worth stating: the certificate is a public document that anybody could copy from a previous connection. Only the signature turns it into a claim, because only the holder of the private key can produce one. ## What the signature actually proves The transcript hash covers every handshake message exchanged up to that moment, on both sides, including values each peer chose freshly for this connection. Three properties follow: - **Possession.** The signature verifies under the public key in the leaf just sent, so the sender holds the corresponding private key. - **Freshness.** Because the transcript is unique to this connection, a `CertificateVerify` recorded from an earlier exchange verifies against nothing here. - **Agreement.** Both peers have signed over the same view of the negotiation, so a party that tampered with an earlier handshake message breaks the signature rather than steering the result. The context string is the part candidates skip. Without it, a signature produced in one role could be taken for a signature in the other; separating the two strings keeps client and server signatures from being interchangeable. ## TLS 1.2 against TLS 1.3 | | TLS 1.2 | TLS 1.3 | |---|---|---| | request parameters | carried directly in the message body, including a list of acceptable issuer names | carried as extensions, including `signature_algorithms(13)` and `certificate_authorities(47)` | | visibility of the client's certificate | sent in the clear, observable on the wire | sent inside the encrypted handshake | | what CertificateVerify covers | the handshake messages exchanged so far | a transcript hash plus a role-specific context string | | asking late in the connection | possible only by renegotiating | `post_handshake_auth(49)`, since renegotiation was removed | ## Reading a failure On a crane's control link that will not come up, this structure tells you where to look. If the client sent no certificate at all, the request either never arrived or nothing the client holds matched the constraints in it. If it sent one and the server rejected it, the leaf reached the server and the objection is about the chain, the validity dates or the signature scheme rather than about selection. The two cases are distinguishable from the handshake alone, which is precisely why knowing which message carries what is an interview question and not trivia.
- Why is a client certificate on its own worthless as proof, even when it chains to a trusted issuer?Because a certificate is public. Anyone who has seen one connection, or read it from a file, can resend the same bytes. The `CertificateVerify` signature is what ties the certificate to the peer actually speaking, because producing it requires the private key and the signature covers this connection's transcript.
- What does certificate_authorities(47) actually change on the client side?It narrows selection. A client holding several certificates uses the listed issuers to choose one likely to be accepted, and a client holding none from those issuers may conclude it has nothing to offer. It is a hint the client should follow, not an enforcement mechanism: the server still validates whatever arrives against its own trust decision.
- What does signature_algorithms(13) in the request constrain?The schemes the client may use for its `CertificateVerify`, which in practice also constrains which of its certificates are usable, since the key type has to support an acceptable scheme. A client whose only certificate holds a key type the server did not list is in the same position as a client with no certificate at all.
saying these in an interview costs you the question
- Thinks the client sends its certificate unprompted on every connection
- Says presenting the certificate alone proves the client's identity
- Believes certificate_authorities(47) is the server's acceptance check
- Places CertificateRequest in the client's flight
- Says CertificateVerify signs the application request that follows