← Week 2: Revocation: CRL and OCSP

Day 14: Challenge — Revoke and Verify Rejection

Phase 2 · June 30, 2026 · Week 2 Review

← Week 2: Revocation: CRL and OCSP

Agenda (2–3 hours)

  • Review (20 min): Week 2 module checklist
  • Integrate (30 min): Wire revocation into validate_chain()
  • Test (90 min): End-to-end revocation scenario
  • Reflect (20 min): What gaps remain?
← Week 2: Revocation: CRL and OCSP

Week 2 State Check

Your toy-pki should now have:

  • [ ] CrlStore data model with revoke() and JSON persistence
  • [ ] CrlStore::generate_and_save() — signed DER CRL output
  • [ ] check_crl_revocation() — verifies CRL and checks a cert's serial
  • [ ] parse_ocsp_request() — extracts CertID from raw DER
  • [ ] build_ocsp_response() — constructs a signed BasicOCSPResponse
  • [ ] Axum OCSP HTTP responder serving live responses
← Week 2: Revocation: CRL and OCSP

Challenge Assignment: Full Revocation Scenario

Write an integration test test_revocation_end_to_end:

1. Build hierarchy: root → intermediate → leaf_a, leaf_b
2. Both certs pass validate_chain() with RevocationPolicy::HardFail
3. Revoke leaf_a with reason=KeyCompromise
4. Regenerate CRL
5. validate_chain(leaf_a, ..., policy=CheckCrl) → Error("revoked: KeyCompromise")
6. validate_chain(leaf_b, ..., policy=CheckCrl) → Ok(())
7. Start OCSP server on a random port
8. Query OCSP for leaf_a → "revoked"
9. Query OCSP for leaf_b → "good"
10. TLS connection attempt using leaf_a (with custom verifier calling check_crl) → rejected
11. TLS connection attempt using leaf_b → succeeds

Steps 10–11 require wiring revocation into a rustls ServerCertVerifier. That's the stretch goal.

← Week 2: Revocation: CRL and OCSP

Custom rustls Verifier with Revocation (Stretch)

use rustls::client::ServerCertVerifier;

struct RevocationCheckingVerifier {
    inner: Arc<dyn ServerCertVerifier>,
    crl_der: Vec<u8>,
    issuer_cert: Vec<u8>,
}

impl ServerCertVerifier for RevocationCheckingVerifier {
    fn verify_server_cert(&self, end_entity, intermediates, ..) -> Result<..> {
        self.inner.verify_server_cert(end_entity, intermediates, ..)?;
        // Parse end_entity cert, check against CRL
        let cert = X509Certificate::from_der(end_entity)?;
        let issuer = X509Certificate::from_der(&self.issuer_cert)?;
        match check_crl_revocation(&cert, &issuer, &self.crl_der, now())? {
            RevocationStatus::Revoked { .. } => Err(rustls::Error::General(...)),
            _ => Ok(...)
        }
    }
}
← Week 2: Revocation: CRL and OCSP

Reflection Questions

Write brief answers before moving to Week 3:

  1. What is the semantic difference between good and unknown in an OCSP response? When should a responder return unknown?
  2. If your OCSP responder is down and you've set HardFail, what happens to legitimate TLS connections? How would you design for availability?
  3. In your team's architecture: who is the right owner of the CRL server — the provisioning service or a separate CA operations team? What are the tradeoffs?
← Week 2: Revocation: CRL and OCSP

Resources

  • rustls ServerCertVerifier trait: docs.rs/rustls
  • RFC 6960 §2.2: OCSP response unknown status semantics
  • Your Day 10 RevocationPolicy implementation