An OCSP response arrives for one lift-pass certificate - what separates its protocol-level status from the certificate status it carries?
answer
- two layers, not one
- the exchange worked versus the certificate is fine
- only successful carries a signed body
- good, revoked, unknown live inside it
- unauthorized tells you nothing about the serial
basics
~20 sThey are two different answers. OCSPResponseStatus reports whether the query itself worked - successful, malformedRequest, internalError, tryLater, sigRequired, unauthorized. Only a successful response carries a signed body with a per-certificate CertStatus of good, revoked or unknown.
solid answer
~40 sAn `OCSPRequest` names a certificate by `CertID` - hashes of the issuer's name and key, plus the serial number. The reply has two layers. The outer `OCSPResponseStatus` is the protocol outcome: `successful`, `malformedRequest`, `internalError`, `tryLater`, `sigRequired` or `unauthorized`. Only `successful` carries a signed `BasicOCSPResponse`, and only that body holds the certificate-level `CertStatus`: `good`, `revoked` with `RevokedInfo` giving the revocation time, or `unknown` with `UnknownInfo`. The trap runs both ways: a `successful` response can perfectly well say `revoked`, and a non-successful one carries no certificate status at all - so treating `tryLater` or `unauthorized` as "not revoked, therefore fine" is an implementation decision, not something the responder told you.
code
http · 12 linesPOST /ocsp HTTP/1.1
Host: status.lift-pass.example
Content-Type: application/ocsp-request
Content-Length: 84
<DER-encoded OCSPRequest: CertID = issuerNameHash, issuerKeyHash, serialNumber>
HTTP/1.1 200 OK
Content-Type: application/ocsp-response
Content-Length: 1573
<DER-encoded OCSPResponse: responseStatus = successful, BasicOCSPResponse>go deeper
Remember that a status query asks about one certificate by serial number and issuer, and that the reply has three possible certificate answers: good, revoked or unknown.
Explain the two layers - the protocol outcome and the signed certificate status - and be able to say why a successful response can still report revoked.
Demonstrate the checks before belief: signature, authorised signer, matching CertID, and the freshness window - and state what your code does when any of them fails.
Recognise that the protocol hands every client the same unanswerable decision about silence, and decide estate-wide what silence means rather than leaving it to each implementation.
## Two answers in one reply RFC 6960 defines a query protocol, and like most query protocols it reports separately on *whether the question was answered* and *what the answer was*. Conflating the two is the single most common defect in hand-written status checking. - **`OCSPResponseStatus`** - the protocol-level outcome. Its values are `successful`, `malformedRequest`, `internalError`, `tryLater`, `sigRequired` and `unauthorized`. Everything except `successful` is an error, is **not signed**, and carries **no certificate status whatsoever**. - **`CertStatus`** - the certificate-level answer, and it exists only inside the signed `BasicOCSPResponse` that a `successful` response carries. Its three states are `good`, `revoked` (with a `RevokedInfo` holding the revocation time and optionally a reason) and `unknown` (with `UnknownInfo`). So: **a successful response can say `revoked`** - success describes the exchange, not the certificate - and an unsuccessful response says nothing about the certificate at all. ## Naming the certificate: CertID A request does not carry the certificate. It carries a `CertID`: a hash algorithm identifier, a hash of the issuer's name, a hash of the issuer's public key, and the certificate's serial number. That combination is what makes a serial number unambiguous - serial numbers are unique only within one issuer - and it is why a responder can answer without ever having seen the certificate that is being presented on the wire. ## What each certificate status actually asserts | status | what it means | what it does not mean | |---|---|---| | `good` | a positive answer for that `CertID` at that time | **not** that the certificate was ever issued, and not that the response time falls inside the certificate's own validity window | | `revoked` | the issuer has withdrawn it; `RevokedInfo` gives the revocation time and may give a reason | not necessarily permanent - a hold can be released, and the revocation time may be in the future for a scheduled revocation | | `unknown` | the responder cannot say - typically it is not authoritative for that issuer | not a denial, and not grounds to treat the certificate as good | The `good` row is worth reading twice. RFC 6960 is explicit that the `good` state is a positive response to the status inquiry and does not by itself assert that a certificate with that serial was ever issued. A responder that answers `good` for any serial it has no record of - the natural naive implementation - is therefore inside the letter of the protocol and useless in practice, which is why responders for public issuance are expected to answer from the issuer's actual database of issued and revoked serials. ## Freshness: the answer is time-boxed Three times appear in the signed body and they are not interchangeable: - **`thisUpdate`** - the time at which the stated status is known to have been correct; - **`nextUpdate`** - the time at or before which newer information will be available; absent means updates are available continuously, which a client should treat with suspicion rather than as permission to cache forever; - **`producedAt`** - when this response was signed, which may be long after `thisUpdate` if the responder pre-produces and caches answers. A client must check the current time against this window. An answer that has passed its `nextUpdate` is stale, and treating a stale `good` as current is how a cached response outlives the revocation it should have reported. ## Replay, caching, and the responder's own certificate Two extensions close the corners: - **`id-pkix-ocsp-nonce`** in a request, echoed in the response, cryptographically binds the answer to that request so a captured old `good` cannot be replayed. RFC 5019's lightweight profile for high-volume environments deliberately drops it - a nonce makes every response unique and therefore uncacheable, and at that scale cacheable pre-produced responses matter more than replay protection. - **`id-pkix-ocsp-nocheck`** on a delegated responder's own signing certificate tells verifiers not to check *its* status. Without it, checking a status requires checking the status of the certificate that signed the status, and so on. Such responder certificates are expected to be short-lived precisely because they cannot be revoked usefully. ## The decision a client actually makes Verify the signature on the response and that the signer is authorised for that issuer; confirm the `CertID` in the response is the one asked about; check the time window; then act on the `CertStatus`. Only when all four hold has the client learned anything. Every other path - a protocol error, an unverifiable signature, a stale window - is the client deciding for itself what to do with silence.
- What do thisUpdate, nextUpdate and producedAt each tell a client?`thisUpdate` is when the stated status was known correct, `nextUpdate` is the time at or before which newer information will be available, and `producedAt` is when this particular response was signed. They differ because responders pre-produce answers: a response signed minutes ago can still describe a status established hours earlier. A client compares the current time against the thisUpdate-to-nextUpdate window and treats anything outside it as stale.
- Why does the high-volume profile in RFC 5019 tell clients not to send a nonce?Because `id-pkix-ocsp-nonce` binds one response to one request, which makes every response unique and therefore impossible to cache or pre-produce. The lightweight profile trades that replay protection for scale: responses are produced in advance, served from ordinary caches, and bounded instead by a required `nextUpdate`. Replay of a stale `good` is then limited by the freshness window rather than by the nonce.
- What stops status checking from recursing into the responder's own certificate?The `id-pkix-ocsp-nocheck` extension on a delegated responder's signing certificate, which tells verifiers not to check that certificate's revocation status. Without it, verifying a status answer would require a status answer about the signer, and so on upward. The trade is that such a certificate cannot be usefully revoked, so it is issued with a short lifetime instead.
saying these in an interview costs you the question
- Reads a successful response as meaning the certificate is good
- Treats tryLater or unauthorized as a certificate status
- Says unknown means the certificate is fine
- Thinks good proves the certificate was actually issued
- Ignores thisUpdate and nextUpdate and caches the answer indefinitely
- Believes the certificate itself is sent in the status request