A TLS 1.3 client holds no certificate matching the server's CertificateRequest - what does it send, and what may the server do?
answer
- silence is not the answer
- an empty answer is still an answer
- nothing to sign, so nothing signed
- the server chooses, the specification permits both
- certificate_required(116) is the refusal
basics
~20 sIt still sends a Certificate message, with an empty certificate_list and no CertificateVerify after it. The server may then either continue the handshake with an unauthenticated peer or abort it with a fatal certificate_required(116) alert.
solid answer
~40 sThe client does not go silent: it sends a `Certificate` message whose `certificate_list` is empty, echoing the request context, and it sends no `CertificateVerify`, because there is no private key to sign with. The handshake is still well formed at that point. The server then has two lawful choices: continue without client authentication, or abort with a fatal `certificate_required(116)` alert. That choice is the whole operational story. A deployment that asks for a certificate but does not require one lets a crane's control link come up anonymous, and the problem surfaces much later as a puzzling rejection at the application layer, or as nothing at all. In TLS 1.2 the equivalent is an empty `Certificate` message, which a server may answer with `handshake_failure(40)`.
code
pseudocode · 15 lines// client side: the CertificateRequest matched nothing it holds
client -> server: Certificate {
certificate_request_context: (echoed from the request)
certificate_list: [] // empty, deliberately
}
// no CertificateVerify: there is no private key to sign the transcript with
client -> server: Finished
// server side, on seeing an empty certificate_list
if client_authentication_required:
send fatal alert certificate_required(116)
close connection
else:
complete handshake
peer_identity = none // the link is up and the caller is unnamedgo deeper
Remember that the client answers even when it has nothing: an empty certificate list is the reply, and no signature follows it because there is no key to sign with.
Explain that the server may continue unauthenticated or abort with certificate_required(116). Both are permitted, so the observed behaviour comes from configuration rather than from the protocol.
Show the production consequence: a green link is not proof of client authentication. Check for a verified peer certificate explicitly, and separate a selection failure from a validation failure before changing anything.
Weigh strictness against availability. Requiring certificates turns every expiry and issuance gap into a refused connection, so the policy has to be decided alongside renewal reliability rather than after it.
## The message a client with nothing still sends When a `CertificateRequest` arrives and the client has no certificate it can use, it does not skip the reply. It sends a `Certificate` message with a zero-length `certificate_list`, echoing the `certificate_request_context` from the request. It then omits `CertificateVerify` entirely, and goes straight to `Finished`. The omission is not an oversight in the specification. `CertificateVerify` exists to prove possession of the private key for a leaf that was just sent; with no leaf, there is nothing to prove and no key to prove it with. Sending an empty certificate is the client's way of answering the question with **no**, in a form the server can parse rather than a silence the server would have to interpret. ## The server's two lawful choices On receiving an empty list, the server may: - **continue the handshake** and treat the peer as unauthenticated, or - **abort** with a fatal `certificate_required(116)` alert. Both are permitted. The specification does not force either, which means the behaviour a deployment sees is a configuration decision and not a protocol fact. This is the single most useful thing to know about the case, because it explains a class of confusion that shows up in production: two services with identical wire behaviour on the client side, one of which comes up and one of which does not. ## Why a link comes up anonymous Asking for a certificate and requiring one are different settings, and the gap between them is where the interesting failure lives. A server that asks but does not require will happily complete a handshake with a crane that presented nothing. The link is up, the operator dashboard is green, and the service is now processing control traffic from a peer whose identity it never established. What happens next depends on the application: 1. If the service reads its caller identity from the certificate, there is none, and every request fails in a way that looks like an application bug rather than a handshake problem. 2. If the service falls back to an identifier in the payload when no certificate is present, the mutual authentication has been silently switched off for that peer, which is much worse than a refused connection. 3. If the service has no fallback and no check, the traffic is simply accepted unattributed. The lesson an interviewer is fishing for: **a successful handshake is not evidence that the client authenticated.** Only rejecting an empty list, or explicitly checking that a verified peer certificate is present, gives you that. ## Why the list came up empty When the client genuinely holds a usable certificate and still sends nothing, the causes are enumerable: - No certificate is installed for that peer at all, or the file it should load is missing. - The `certificate_authorities(47)` hint in the request named issuers, and nothing the client holds was issued by one of them, so selection produced no candidate. - The `signature_algorithms(13)` list in the request excluded every scheme the client's key can produce, so its certificate is unusable even though it exists. - The certificate the client holds is outside its validity period, and the client declined to offer it. All four look identical from the server: an empty `certificate_list`. Distinguishing them means looking at what the request asked for, which is why the request's extensions are worth reading rather than skipping. ## Version differences that change the alert | | TLS 1.2 | TLS 1.3 | |---|---|---| | what the client sends | a `Certificate` message with no certificates in it | a `Certificate` message with an empty `certificate_list` and the request context echoed | | what follows it | no `CertificateVerify` | no `CertificateVerify` | | how a strict server refuses | a fatal `handshake_failure(40)` | a fatal `certificate_required(116)` | | is refusal mandatory | no, the server may continue | no, the server may continue | The dedicated alert in TLS 1.3 is a genuine improvement for diagnosis. `handshake_failure(40)` is the generic refusal that also covers a version mismatch, an unusable group and a dozen other disagreements; `certificate_required(116)` says exactly one thing, and a peer that receives it knows the problem is its own missing certificate rather than anything about the negotiation.
- Why does the client send an empty Certificate message rather than simply omitting it?Because the flight has a defined shape and the server is waiting for a specific message. An explicit empty list is an unambiguous answer of no, parsed like any other reply, while an omitted message would be a protocol violation the server would have to guess at. It also lets the client echo the request context, keeping request and answer paired.
- A mutual link comes up successfully but the service sees no caller identity. Where do you look first?At whether the server required a certificate or merely requested one. A completed handshake proves nothing about client authentication when an empty list is accepted, so the first check is whether a verified peer certificate is actually present on the connection, and only then whether the client had one to offer.
- The client holds a valid certificate and still sends an empty list. What in the request could explain it?Either the issuer hint or the scheme list. If `certificate_authorities(47)` named issuers that did not include the certificate's own, selection produced no candidate; if `signature_algorithms(13)` excluded every scheme the client's key supports, the certificate is unusable regardless of who issued it. Both look identical from the server's side.
saying these in an interview costs you the question
- Says the client sends nothing at all and lets the server time out
- Thinks the server must abort when the certificate_list is empty
- Believes a completed handshake proves the client authenticated
- Expects a CertificateVerify after an empty certificate_list
- Assumes certificate_required(116) exists in TLS 1.2 as well