In TLS 1.3, what does the server's CertificateVerify signature cover, and what does it prove that its Certificate message cannot?
answer
- signs the conversation, not the document
- the client's own random is inside
- certificates are public, signatures are not
- key of the first CertificateEntry
- Finished proves the exchange, not identity
basics
~20 sIt signs a value derived from the transcript of the handshake so far, not the certificate bytes. That binds possession of the first entry's private key to this specific handshake, which a copied Certificate message — public data anyone can replay — can never do.
solid answer
~40 s`CertificateVerify` carries a signature over a value derived from the **handshake transcript**: every handshake message exchanged up to that point, including the client's own freshly generated `ClientHello`. It is made with the private key of the certificate in the first `CertificateEntry`, and the message names the signature scheme used, such as `ecdsa_secp256r1_sha256(0x0403)` or `rsa_pss_rsae_sha256(0x0804)`. Two properties follow. First, **possession**: only the holder of that private key can produce the signature, so presenting a certificate copied from anywhere is not enough. Second, **freshness**: because the client's own random contribution is inside the transcript, a signature recorded from an earlier handshake will not verify in this one. The `Certificate` message supplies neither — it is a public document that any party can transmit.
code
pseudocode · 13 lines# server side
transcript = all handshake messages sent and received so far
signed_input = padding_prefix + context_string + hash(transcript)
signature = sign(private key of certificate_list[0], signed_input)
send CertificateVerify(15) with { algorithm, signature }
# client side
recompute transcript from the same messages
if algorithm was not offered in this client's signature_algorithms(13):
abort
if not verify(public key of certificate_list[0], signed_input, signature):
abort
# only now does the chain in Certificate(11) describe THIS peergo deeper
Hold onto the shape: the certificate is a public claim, and the signature is the private proof that goes with it. Anyone can show the claim; only the key holder can produce the proof.
Explain that the signed input is the handshake transcript including the client's own random contribution, and that this gives both possession and freshness in one step.
Be able to separate a chain failure from a signature failure on sight, and say why Finished does not substitute — that distinction is what makes an active-attacker analysis come out right.
Frame it as where the long-term private key is exercised: one signature per full handshake, which is what makes handshake rate, key custody and resumption policy a single coupled decision.
## What is actually signed The input to the signature is **not** the certificate and **not** the application data. It is a value derived from the running transcript of the handshake — the concatenated handshake messages both peers have exchanged so far, hashed, then wrapped with a fixed padding prefix and a context string that names which side is signing. The signature is produced with the private key belonging to the certificate in the **first** `CertificateEntry` of the `certificate_list` the server just sent. Two details of that input do all the work: - The transcript contains the **client's** `ClientHello`, including a value the client generated for this connection alone. - The context string distinguishes a server's signature from a client's, so one cannot be substituted for the other. ## The two properties it establishes 1. **Possession.** A valid signature can only be produced by whoever holds the private key that the first certificate names. This is the point of the whole exercise: the certificate says "this public key belongs to this name", and `CertificateVerify` says "and I am the party holding the matching private key". 2. **Freshness and binding.** Because the client's own contribution is inside the signed transcript, the signature is valid for *this* handshake only. A signature captured from a recorded session cannot be replayed into a new one, since the transcript differs from the first message onward. ## Why the Certificate message cannot supply either | Question | Answered by `Certificate` | Answered by `CertificateVerify` | |---|---|---| | Which name and public key is being claimed? | yes | no | | Who vouched for that binding? | yes, via the issuing chain | no | | Does this peer hold the matching private key? | no | yes | | Is this evidence tied to this connection? | no | yes | A certificate is public by design. It is handed to every client that connects, it can be pulled from public logs, and anyone who recorded an older handshake has a copy. An attacker can therefore always present a legitimate certificate. What the attacker cannot do is sign this handshake's transcript with the key it names. ## Why `Finished` is not the same guarantee This is the most common substitution error. `Finished` is computed over the transcript too, but with a key derived from the **key-exchange** secret, not from the certificate's private key. It proves both peers derived the same secret and that the transcript was not tampered with — a strong property, but one an active attacker who performed its own key exchange with the client also satisfies. Only `CertificateVerify` ties that key exchange to the identity in the certificate. Remove it and the handshake still completes; it just authenticates nobody. ## The scheme identifier inside the message `CertificateVerify` carries the signature scheme alongside the signature, drawn from the same registry the client used in its `signature_algorithms(13)` extension. Values look like: - `rsa_pkcs1_sha256(0x0401)` - `ecdsa_secp256r1_sha256(0x0403)` - `rsa_pss_rsae_sha256(0x0804)` - `ed25519(0x0807)` The server must pick a scheme the client offered. Choosing one the client never advertised is as fatal as producing an invalid signature, because the client has no obligation — and may have no ability — to verify it. ## Boundaries worth stating explicitly - It says nothing about whether the certificate is **valid**: expiry, name matching and building a path to a trust anchor are separate checks the client performs on the `Certificate` message contents. - It says nothing about **revocation**. A revoked certificate's key still produces a perfectly valid signature. - The **client's** own `CertificateVerify`, sent in answer to a `CertificateRequest`, works the same way over the transcript as it stands at that point, but it is a distinct message with a distinct context string and belongs to client authentication. - In TLS 1.2 this message existed only on the client side; a TLS 1.2 server's equivalent proof was the signature it placed in `server_key_exchange (12)`. The practical payoff: when a handshake fails, "the chain is wrong" and "the signature is wrong" are different diagnoses with disjoint fixes. Re-issuing a certificate will never repair a server that is signing with a scheme the client did not offer, and no amount of signature debugging will supply a missing intermediate.
- Could an attacker replay a recorded Certificate and CertificateVerify pair to impersonate the server?The `Certificate` message replays fine — it is public data. The `CertificateVerify` does not. Its signature covers a transcript that starts with the client's own `ClientHello`, and this client generated fresh values for this connection, so the recorded transcript and the current one differ from the first message. The recorded signature therefore fails verification, and the attacker cannot produce a new one without the private key.
- The Finished message is also computed over the transcript. Why is CertificateVerify still needed?`Finished` is keyed from the key-exchange secret, so it proves both peers derived the same secret and that no message was altered. An active attacker that ran its own key exchange with the client satisfies it perfectly well. Only `CertificateVerify` binds that exchange to the private key named by the certificate, which is what turns an encrypted channel into an authenticated one.
- Can the server sign with whichever scheme it prefers?No. The scheme is carried in the message and must be one the client advertised in `signature_algorithms(13)`. A client that never offered a scheme may not be able to verify it at all, and is entitled to reject a signature that uses one. In practice this constrains which certificate the server can usefully select in the first place.
A sealed letter of introduction can be photocopied by anyone who has seen it. Asking the bearer to sign a sentence you have just dictated cannot be — and the sentence has to be one you made up on the spot, or last week's signature would do.
saying these in an interview costs you the question
- Says the signature covers the certificate bytes
- Thinks Finished already proves private-key possession
- Believes a copied certificate is enough to impersonate a server
- Assumes the server may sign with any scheme it likes
- Claims the signature also proves the certificate is unrevoked