Windows OCSP cache retention
An OCSP entry in Windows CryptnetUrlCache does not have one fixed lifetime. Protocol validity and physical file retention are controlled by different clocks.
- Validation lifetime: how long the response can satisfy a revocation check.
- Physical retention: how long its
MetaDataandContentfiles can remain recoverable.
if the response contains nextUpdate:
valid_until = min(nextUpdate, certificate.NotAfter)
else:
valid_until = min(thisUpdate + OcspValiditySeconds, certificate.NotAfter)
flush_base = max(ExpireTime, LastSyncTime)
flush_eligible = flush_base + flush_exemption
The default OcspValiditySeconds is 43,200 seconds (12 hours). A stored dwExemptSeconds of zero selects the service default, normally 2,419,200 seconds (28 days). 0xFFFFFFFF disables automatic flushing for the entry.
Reverse-engineering scope
The result was established from these installed Windows 11 binaries, imported into Ghidra 12.2 with matching Microsoft public symbols:
cryptnet.dll- Version
10.0.26100.9549
SHA-256E478DD3D872606A1B0CBB4B50BED941EA8ACA92B71DF4178CA7B164AAFB7428A cryptsvc.dll- Version
10.0.26100.1
SHA-256078C8645A3F57BBF5ABA4E55C9989C400CC2E6B39B886F9B5A1AD7435B1D1500
Addresses below apply only to these images. Other Windows releases may differ.
Response validity
cryptnet!OcspConvertResponseToCrl at 0x180009EC0 converts an accepted OCSP response into the time-valid-object representation used by Windows. It uses the response's nextUpdate when present. If the field is absent, it adds the value returned by I_GetOcspValiditySeconds at 0x18001CC38 to thisUpdate. In both cases it caps the result at the subject certificate's NotAfter time.
I_GetOcspValiditySeconds reads the OcspValiditySeconds DWORD under:
HKLM\Software\Microsoft\Cryptography\OID\EncodingType 0\
CertDllCreateCertificateChainEngine\Config
An absent or invalid value produces the 12-hour default. HTTP Cache-Control: max-age, parsed elsewhere by cryptnet.dll, is response and revalidation metadata; it does not replace OCSP protocol validity.
On-disk fields that control retention
The current 116-byte metadata prefix contains the following retention fields. This is an observed implementation layout, not a documented file-format ABI.
| Offset | Field | Use |
|---|---|---|
0x10 | LastSyncTime | Last successful cache retrieval or update. |
0x18 | Prefetch structure size | Validates the prefetch block. |
0x1C | Prefetch object type | 3 denotes OCSP. |
0x20 | Prefetch error | Zero permits active-prefetch handling. |
0x28 | Prefetch ThisUpdateTime | Status interval start. |
0x30 | Prefetch NextUpdateTime | Status interval end and refresh input. |
0x40 | Flush structure size | Validates the flush block. |
0x44 | dwExemptSeconds | Post-base retention exemption. |
0x48 | ExpireTime | Expiry used by the normal flush calculation. |
The flush block corresponds to Microsoft's CRYPTNET_URL_CACHE_FLUSH_INFO.
Normal eviction
The deletion decision is made in cryptsvc!EnumUrlCacheEntryCallback at 0x1800020A0. For a non-active-prefetch entry, the service:
- Uses the stored exemption, or the machine default when it is zero.
- Leaves the entry alone when the exemption is
0xFFFFFFFF. - Chooses the later of
ExpireTimeandLastSyncTime, with clock-rollback handling for a future synchronization time. - Adds the exemption and queues an eligible pair for deletion.
cryptsvc!InitPreFetchPara at 0x18000CD20 initializes the default exemption to 0x24EA00, or 28 days. A nonzero CryptnetDefaultFlushExemptSeconds DWORD under the chain-engine Config key can override it.
Queued entries are deleted by FlushUrlCacheEntries at 0x180008E04 during a service scan. The paired content and metadata files are separate; explicit deletion removes content first, so interruption or collection timing can leave an orphaned half-pair.
Active prefetch
An entry with a valid prefetch block, nonzero object type, zero prefetch error, and nonzero NextUpdateTime follows a different branch. The service creates or updates a prefetch job instead of placing the entry on the normal flush list.
Successful background refresh can advance LastSyncTime and replace validity metadata without the original application—or malware—running again. The normal formula is therefore not an upper bound for an actively prefetched entry. If prefetch is unsupported, malformed, or records an error, the entry falls back to normal time-based flushing.
Worked forensic windows
Assume the initial retrieval occurred at T0, thisUpdate is close to retrieval time, and the default 28-day exemption applies:
| Scenario | Validation expiry | Normal deletion eligibility |
|---|---|---|
nextUpdate = T0 + 7 days | About day 7 | About day 35 |
No nextUpdate | About 12 hours after thisUpdate | About day 28.5 |
nextUpdate = T0 + 60 days | About day 60 | About day 88 |
| Active prefetch | Refresh advances it | No fixed upper bound |
Flush disabled (0xFFFFFFFF) | Response expires | No automatic flush |
Use the actual FILETIME and OCSP values when analysing a real entry.
Live cache examples
A read-only scan on 2026-11-05 found 60 OCSP-bearing entries in the current user's cache. It used cryptnet-dump.exe --ocsp-only --json without --resolve; no certificate serials or URLs were sent to an external service. Times below are UTC.
Thirty-day-old normal entry
- Responder
oneocsp.microsoft.com- Cache key
BBD4B1348E8664F092F68378AEE49761_FEB2D449327D68824E2066BF592EFC1B- LastSyncTime
- 2026-10-06 10:20:37
- ExpireTime / NextUpdateTime
- 2026-10-12 20:20:20
- Flush path
- Stored exemption zero; prefetch error 50 (
ERROR_NOT_SUPPORTED) - Normal eligibility
- 2026-11-09 20:20:20
The roughly 30-day-old files were still present because the post-expiry exemption had not elapsed.
Recent normal entry
- Responder
ocsp.digicert.com- Cache key
8EC9B1D0ABBD7F98B401D425828828CE_0B1450752E8709914F773207984E4A63- LastSyncTime
- 2026-11-04 23:56:35
- ExpireTime / NextUpdateTime
- 2026-11-11 20:21:00
- Flush path
- Prefetch error 50; normal flushing
- Normal eligibility
- 2026-12-09 20:21:00
ExpireTime exactly matched the OCSP response's nextUpdate; the default exemption then added 28 days.
Long-lived active-prefetch candidate
- Responder
ocsps.ssl.com- Cache key
161832C90068D02CF65B25D285AB768A_3E5CCA2E9839DA0DDB183F1D9E86F21F- LastSyncTime
- 2026-11-05 00:13:27
- ExpireTime / NextUpdateTime
- 2027-06-07 17:45:56
- Prefetch status
- Error zero; active-prefetch conditions satisfied
- Normal-formula date if inactive
- 2027-07-05 17:45:56
This entry demonstrates both a long responder validity interval and the active-prefetch branch. The normal-formula date is not an upper bound while prefetch remains active.
Forensic interpretation
Presence establishes that certificate-related material was cached for that profile. It does not identify the initiating process, prove the inferred site was contacted, show that TLS succeeded, or establish that malware ran at LastSyncTime. OCSP retrieval may relate to a TLS certificate, intermediate CA, Authenticode signer, timestamp chain, or another validation operation.
Absence is also weak evidence. The entry may never have been created, may belong to another profile, may have been cleared manually, may have used stapled OCSP or another TLS stack, or may already have been scanned and deleted. Treat flush_eligible as a lower bound on automatic deletion, never as a guaranteed deletion timestamp.