In Certificate Transparency, why does a CA log a precertificate with a critical poison extension rather than the certificate it will issue?
answer
- the timestamps change the signed bytes
- log before issue, embed after
- a signed object that must never validate
- critical and unrecognised, therefore fatal
- poison out, SCT list in
basics
~20 sEmbedding the returned timestamps changes the certificate's signed bytes, so the certificate cannot be logged before it exists. The authority logs a precertificate — the same content plus a critical poison extension, OID 1.3.6.1.4.1.11129.2.4.3, that makes it unusable — then issues the real one.
solid answer
~40 sThe embedding path has a circularity: the timestamps have to be *inside* the certificate, but they only exist once something has been submitted to a log, and submitting the finished certificate would mean the bytes change afterwards and the issuer's signature no longer covers them. A **precertificate** breaks the cycle. It contains the same `TBSCertificate` content the authority is about to issue, plus a critical extension at OID `1.3.6.1.4.1.11129.2.4.3` — the **poison extension** — whose only purpose is to guarantee no verifier will ever accept the precertificate as a real one. The logs record that, return their `SignedCertificateTimestamp` values, and the authority then signs the final certificate with the poison extension replaced by the SCT list extension at OID `1.3.6.1.4.1.11129.2.4.2`.
code
pseudocode · 11 linestbs = build_tbs_certificate(subject_names, public_key, validity)
precert = sign_with_issuer_key(tbs + critical_extension("1.3.6.1.4.1.11129.2.4.3"))
sct_list = empty list
for each log in chosen_logs:
sct_list.append(submit_precertificate(log, precert))
final_tbs = tbs + extension("1.3.6.1.4.1.11129.2.4.2", sct_list)
final_certificate = sign_with_issuer_key(final_tbs)
return final_certificatego deeper
Recall that the authority logs a throwaway version of the certificate first, and that the real one is signed afterwards with the timestamps inside it.
Explain the circularity precisely: the timestamps must be in the signed bytes, but they only exist after submission, so a separate poisoned object is submitted instead.
Point out the operational edges — an abandoned precertificate entry is still evidence of intent, and embedded timestamps are frozen at issuance, so changing logs means reissuing.
Judge the trade the design makes: convenience for operators who configure nothing, paid for with a second signed object per issuance and a delivery choice fixed at issuance time.
## The ordering problem A certificate is a signed object: the issuer signs the `TBSCertificate` and any change to those bytes invalidates the signature. That collides directly with the most convenient way of delivering signed certificate timestamps, which is to carry them **inside** the certificate as a v3 extension, because then every client gets them with no server configuration at all. Put the two requirements together and you get a cycle: 1. The timestamps have to be inside the certificate. 2. A timestamp only exists once a log has been given something to log. 3. If the thing logged were the finished certificate, adding the timestamps to it would change the bytes and produce a *different* certificate — one nobody logged. Something has to be logged before the certificate exists. That something is the precertificate. ## What a precertificate is A **precertificate** is a signed object built from exactly the content the authority intends to issue — same subject, same subject alternative names, same public key, same validity window — with one addition: a **critical** extension at OID `1.3.6.1.4.1.11129.2.4.3`, known as the **poison extension**, carrying no meaningful value. Its entire job is to be fatal. The extension is marked critical and no verifier recognises it, so a conforming verifier rejects the object rather than accepting an extension it cannot process. The result is a signed object that is safe to publish and impossible to use as a server certificate. That matters, because the precertificate is a fully signed statement about a real name and key: without the poison, the authority would be publishing a second usable certificate for every one it issues. ## The sequence 1. The authority assembles the intended content and signs it with the poison extension added. 2. It submits that precertificate to several logs its clients' policies recognise. 3. Each log records the entry and returns a `SignedCertificateTimestamp`. 4. The authority builds the final `TBSCertificate`: poison extension removed, an **SCT list** extension at OID `1.3.6.1.4.1.11129.2.4.2` added, carrying the collected timestamps. 5. It signs that, and the subscriber receives a certificate that already carries its own transparency evidence. | | precertificate | final certificate | |---|---|---| | carries | the critical poison extension | the SCT list extension | | usable in a handshake | no, by construction | yes | | exists to | be logged before issuance | be served to clients | | logged as | a precertificate entry | may also be logged directly | ## What a verifier does with the pair A client that sees the final certificate never sees the precertificate. It reads the SCT list extension, and to check each timestamp's signature it reconstructs what the log actually signed — the precertificate content, with the SCT list extension it is currently looking at stripped back out. That reconstruction is why the two objects must agree on everything except those two extensions: an authority that logged one set of names and issued another would produce timestamps that fail to verify. Practical consequences worth holding on to: - **The log entry is the honest record even if the certificate is never delivered.** An authority that logs a precertificate and then abandons issuance has still published its intent, and a monitor will see it. A precertificate entry for a name you own deserves the same attention as a final certificate. - **Embedding is a one-shot decision.** The timestamps are frozen into the signed bytes at issuance; changing which logs a certificate cites means getting a new certificate. - **A certificate without an embedded SCT list was not necessarily unlogged.** Two other delivery paths exist, and an operator may be using one of them. - **The poison extension is never seen by clients.** If one ever turns up in a certificate served in a handshake, that certificate is broken, and a conforming verifier will refuse it rather than ignore the extension. ## Why not just skip embedding? Because embedding is the only path that requires nothing of the server. The other delivery mechanisms need the server to hold and serve the timestamps itself, which is one more piece of state to configure and keep in step with renewal. Having the authority bake them in at issuance makes transparency work for operators who never configure anything — and the precertificate with its poison extension is the price of that convenience.
- What would go wrong if the poison extension were marked non-critical?A verifier is free to ignore an extension it does not recognise when that extension is not critical. The precertificate would then validate like an ordinary certificate for the same name and key, and every issuance would publish a spare usable certificate. Criticality is precisely what turns the object into one nobody can use.
- A monitor alerts on a precertificate entry for a name you own, and no matching certificate ever appears. Does that matter?Yes. The precertificate is a signed statement that an authority was about to issue for that name, so the intent is real whether or not the final certificate was delivered. Treat it exactly as you would a final-certificate entry: establish whether it came from your own automation before calling it mis-issuance.
saying these in an interview costs you the question
- Says the authority logs the finished certificate and then adds the timestamps.
- Thinks a precertificate can be served to clients in a handshake.
- Assumes the poison extension is what carries the timestamps.
- Believes nothing is recorded until the final certificate is signed.
- Assumes a certificate with no embedded SCT list was never logged.