skip to content

What do the TLS alerts unknown_ca(48), certificate_expired(45) and bad_certificate(42) each tell you about the validating peer's verdict?

level: juniorimportance: must knowfreq 70%

answer

  1. three verdicts on one certificate_list
  2. the validator speaks, not the owner
  3. anchor, dates, bytes
  4. unknown_ca(48) is a reachability verdict
  5. certificate_expired(45) includes not-yet-valid

basics

~20 s

unknown_ca(48) means what arrived never reached a trust anchor the validator holds. certificate_expired(45) means a certificate it needed is outside its validity window. bad_certificate(42) means a certificate was corrupt or its signature did not verify.

solid answer

~40 s

All three are error alerts from the peer that just validated a chain, so in an ordinary server-authenticated handshake they come from the client, and they can only arrive after a `Certificate` message has been processed. `unknown_ca(48)` says the validator could not tie what it received to a trust anchor it holds - either the anchor is absent from its store, or the `certificate_list` was too short to reach one. `certificate_expired(45)` says some certificate the validator needed is outside its validity window; it covers not-yet-valid as well as expired, and an intermediate counts as much as the end-entity leaf. `bad_certificate(42)` is the structural verdict: corrupt bytes or a signature that failed. None of the three names which certificate in the list failed.

code

pseudocode · 11 lines
pseudocode
function validate_received_chain(certificate_list):
    for each certificate in certificate_list:
        if decode(certificate) fails or signature does not verify:
            return alert bad_certificate(42)
        if now is outside validity_window(certificate):
            return alert certificate_expired(45)

    if no trust anchor held locally matches certificate_list:
        return alert unknown_ca(48)

    return accept

go deeper

for a junior

Learn the three descriptions as three different verdicts: no anchor reached, a date out of range, bad bytes or a bad signature. Being able to say which of those you were told is most of the value.

for a middle

Explain that the alert comes from whichever peer just validated a chain, and that it can only follow a Certificate message. Show that certificate_expired(45) covers not-yet-valid and applies to intermediates too.

for a senior

Demonstrate the next move rather than the definition: on unknown_ca(48) compare two validators' anchors, on certificate_expired(45) check every certificate's dates and the clock, and say why a verdict is not yet a cause.

for a principal

The angle is what these verdicts cost an estate: how much of an outage is spent discovering that an intermediate, not a leaf, aged out, and whether the fleet's validators are consistent enough that one component's acceptance predicts another's.

## What kind of statement these three alerts are A TLS peer that has just validated a certificate chain has exactly one way to say *why* it is giving up: it sends an error alert and closes. `unknown_ca(48)`, `certificate_expired(45)` and `bad_certificate(42)` are three different verdicts on the same input - the `certificate_list` the other peer put on the wire - and each eliminates a different set of causes. Two facts frame all three: - **The sender is the validating peer, not the certificate's owner.** In an ordinary server-authenticated handshake that is the client. The peer holding the private key does not send these about its own chain. - **They can only arrive after a `Certificate` message has been processed.** An alert that arrives earlier cannot be a verdict on a certificate at all, whatever wording the failing application prints. ## unknown_ca(48) - nothing reached an anchor the validator holds `unknown_ca(48)` says a valid chain or partial chain was received but could not be tied to a trust anchor the validator holds. Two very different causes produce it: 1. **The validator's store does not contain the anchor.** An internal authority installed for one runtime and not another is the classic case, and it is why the same chain is accepted by one component and refused by another on the same host. 2. **What arrived was not enough to reach an anchor.** If only the end-entity certificate was supplied and the validator held no copy of the issuer, it has a partial chain and no way to finish it. Notice what the alert does *not* say. It does not say the certificate is forged, and it does not say it is the wrong certificate. It says *this* validator could not get from what it was given to something it already trusted. ## certificate_expired(45) - a validity window, anywhere in the list `certificate_expired(45)` means a certificate has expired or is not yet valid. Two details are routinely missed: - It covers **not yet valid** as well as expired, so a validator whose clock is behind produces it against a certificate issued minutes ago. - It applies to **any** certificate the validator needed, not only the end-entity leaf. An intermediate that has aged out produces the same alert as a leaf nobody renewed - and the renewal you deployed on the leaf changes nothing. ## bad_certificate(42) - the bytes or the signature `bad_certificate(42)` is the structural verdict: a certificate was corrupt, or it carried a signature that did not verify. It is about the object itself rather than about trust or about time, which also makes it the least specific of the three in practice. ## Reading them side by side | Alert | The validator concluded | It does not tell you | |---|---|---| | `unknown_ca(48)` | Nothing I received reaches an anchor I hold | Whether the anchor is absent or the list was short | | `certificate_expired(45)` | A certificate I needed is outside its validity window | Which certificate, or whose clock is wrong | | `bad_certificate(42)` | A certificate was corrupt or its signature failed | Which field, or which certificate in the list | ## What to check first, per verdict 1. On `unknown_ca(48)`, compare the anchors the two validators hold before touching the peer that served the chain - one of them accepted it. 2. On `certificate_expired(45)`, check the dates of every certificate in the list and the validator's own clock, in that order. 3. On `bad_certificate(42)`, get the bytes the peer actually sent, because a truncated or re-encoded copy is a far more common cause than a genuinely bad signature. ## Where the mapping stops - **No alert names a position in the `certificate_list`.** The description identifies the verdict, never the certificate. - **A peer is not obliged to be specific.** An implementation that answers every validation failure with `handshake_failure(40)` is still a working peer, so the absence of a certificate-shaped alert is not evidence that certificates were fine. - **A verdict is not a cause.** `unknown_ca(48)` is where the investigation starts, not where it ends. That last point is the whole skill. An interviewer asking this is not testing whether you can recite three numbers; they are testing whether hearing one of them narrows your next question down to a single comparison.

  • The same chain is accepted by one client and refused with unknown_ca(48) by another on the same host. What differs?
    Not the bytes on the wire - the two validators do. They consult different sets of trust anchors, so an authority present in one store and absent from the other decides it. A validator that already held the issuing intermediate can also finish a path that a validator seeing only the end-entity certificate cannot. Compare the two stores first; nothing about the peer that served the chain has changed.
  • Why can certificate_expired(45) appear minutes after a renewal you confirmed was deployed?
    Because the alert is not scoped to the certificate you renewed. An intermediate in the same list can be the one outside its window, a second front end may still be serving the previous list, and a validator whose clock runs behind produces the same description against a certificate that is not yet valid. Check every certificate's dates and the validator's clock before re-issuing anything.

Three reasons a doorman refuses a letter of introduction: he does not recognise the signatory, the letter is dated for last year, or the seal is broken. Each refusal sends you somewhere different next.

saying these in an interview costs you the question

  • Says unknown_ca(48) means the certificate is forged or self-signed
  • Reads certificate_expired(45) as always about the end-entity leaf
  • Assumes the server sends these because it owns the certificate
  • Thinks the alert identifies which certificate in the list failed
  • Treats a generic handshake_failure(40) as proof the certificates were fine