OCSP responder — RFC 6960 revocation for CodeB signing certs.
Every CodeB tenant runs an in-house RFC 6960 OCSP responder at POST /csc/v2/ocsp. It answers CertID queries for every certificate the tenant issues — per-user signing certs, the TSA responder cert, and the OIDC / VCI issuer certs — and returns a BasicOCSPResponse signed by the tenant's own responder key. The browser signer (sign.html) uses this endpoint to embed id-aa-ets-revocationValues inside every PAdES-B-LT and PAdES-B-LTA envelope, so relying parties can verify signatures offline after the signer's cert expires.
Scope reminder. This responder is self-signed by the tenant — it is not a qualified OCSP responder under Regulation (EU) 910/2014. It exists to complete the local trust story for the in-tenant PKI: the CodeB-issued certs point back to a CodeB-signed OCSP responder, and the whole chain is verifiable without reaching any external service.
Full RFC 6960 wire protocol. No Bearer required — RFC 6960 §A.1 describes the OCSP request/response over HTTP and by long-standing convention responders are open. Host-level rate limits still apply (30 req/min per action per IP).
Request
Content-Type: application/ocsp-request
Body: DER-encoded OCSPRequest (RFC 6960 s4.1.1)
(SEQUENCE tbsRequest, optional SEQUENCE optionalSignature)
tbsRequest.requestList contains one or more Request
{ CertID reqCert, extensions singleRequestExtensions? }
Response
Content-Type: application/ocsp-response
Body: DER-encoded OCSPResponse (RFC 6960 s4.2.1)
{ responseStatus INTEGER,
responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
ResponseBytes.responseType == id-pkix-ocsp-basic (1.3.6.1.5.5.7.48.1.1)
ResponseBytes.response is the DER of BasicOCSPResponse.
vci/ — the PID / SD-JWT VC issuer cert used by vci.ashx
The index is keyed by the SHA-1 issuerNameHash + serialNumber tuple — the same CertID a requester sends. A miss returns certStatus = unknown per RFC 6960 §4.2.2. This is a deliberate choice: we would rather say "we don't know about that cert" than lie and say "good".
Refresh
The cache reloads on every request older than 60 seconds and on tenant restart. Newly-issued per-user signing certs are picked up on the next request; there is no separate "publish to OCSP" step. New TSA certs (from the auto-generated TSA issuer) are picked up the same way.
Requests use SHA-1 CertID hashes per RFC 5019 for interoperability with off-the-shelf OCSP clients. The responder normalises algorithm mismatches (SHA-256, SHA-1) internally so a requester that follows either RFC 6960 or RFC 5019 gets a valid answer.
Nonce extension (RFC 6960 §4.4.1) is echoed when present; absent when the request does not carry one. Responses are freshly built per request (no caching of signed responses) so producedAt always reflects real time.
PAdES-B-LT use case
The browser signer fetches OCSP for both the signer cert and the TSA cert immediately after the RFC 3161 timestamp comes back, then embeds both as id-aa-ets-revocationValues.
Signer OCSP fetch
TSA OCSP fetch
Bundle into CMS unsignedAttrs
Relying party validates offline
Direct client use case
Any RFC 6960 client (openssl, xmlseclibs, iText, pyHanko) can point at the endpoint. It speaks the wire protocol; no CSC-specific glue required.
The responder signs with an EC P-256 key using ECDSA-with-SHA-256 (1.2.840.10045.4.3.2). The response's certs [0] optional element embeds the responder's own certificate so verifiers can chain-build offline.
Delegated signing
By default the responder is the tenant CA: ResponderID.byKey points at the tenant CA's SubjectPublicKeyInfo hash, and it signs directly. This matches RFC 6960 §4.2.2.2 delegated-responder rules trivially, because the CA and the responder key are the same key.
Operator note: a future release will let you point Ocsp:ResponderCert=<path> at a separate delegated responder cert (issued by the CA and carrying EKU id-kp-OCSPSigning) so day-to-day OCSP signing does not use the root CA key. The current setup is acceptable for AdES scope; QES scope would require the delegated model.
Auto-generation
If App_Data/<tenant>/signing-cert/tenant-ca.pfx is missing at startup, the tenant generates a fresh EC P-256 CA cert (5-year validity, BasicConstraints CA:TRUE critical, KeyUsage keyCertSign+cRLSign+digitalSignature) and uses that for both issuance and OCSP signing.
Revocation is a flat file: App_Data/<tenant>/csc-revoked-serials.json. Each entry is {serial_hex, reason, revoked_at}. The responder consults this list before answering; a hit returns certStatus = revoked with a revocationTime; a miss returns good for known certs and unknown for anything the responder does not know about.
The list is loaded lazily and re-read on modification-time change. There is no separate CRL to publish; the whole revocation surface is this one endpoint.
Metrics tags: status = good|revoked|unknown; cert_source = one of the four scanned directories. Rejections emit [METRIC] csc.ocsp.rejected reason=<badRequest|malformedRequest|internalError|tryLater|sigRequired|unauthorized> aligning with the six RFC 6960 responseStatus codes.
Rate limit
Same per-IP bucket as the rest of /csc.ashx: 30 requests per minute per action. Over-limit returns HTTP 429 with a Retry-After: 30 header and a tryLater OCSP body.