DEV Community

AdminPackStudio
AdminPackStudio

Posted on

Revocation Server Was Offline (0x80092013): A Read-First CRL, CDP, Cache, and Offline Root Triage

Helpdesk: "Since about 7:30 this morning the 802.1X Wi-Fi rejects every laptop, the VPN says the certificate couldn't be validated, and the issuing CA service won't start. Its error says the revocation function was unable to check revocation because the revocation server was offline, 0x80092013. The PKI admin is out; the offline root has been powered off in a closet since spring. A forum post says to set CRLF_REVCHECK_IGNORE_OFFLINE on the CA, set NoRevocationCheck on both NPS servers, and turn off revocation checking in the VPN profile. Can we start with that?"

Not yet. "Revocation server offline" almost never means a server is down. It means a client building a certificate chain couldn't get a current CRL for one of the certificates in it: the CRL expired, the CDP URL can't be reached from that client's network or security context, the URL was never published, or the client is still using a stale cached copy. Microsoft's DirectAccess article for 0x80092013 lists those four causes word for word, starting with "The CRL location (CDP) is unreachable." and ending with "The CRL is reachable but the client is picking from an old cache." Its chain-validation note explains the blast radius: "Revocation checking is performed from the root CA certificate down to the evaluated certificate." One expired root CRL breaks every certificate under it, and Wi-Fi, VPN, and the CA itself fail together. Turning off revocation checking hides the failed link and leaves it failed, and a CRL that's still expired tomorrow breaks the next app nobody exempted. Work through it as a chain: which app, which error code, which certificate, which context, since when → certutil -verify -urlfetch on that certificate, from the failing machine → which CRL failed: issuing, root, or delta, and its NextUpdate → can this client, in this context, reach the CDP URL? → does the web or LDAP location serve the current file? → CAPI2 events, which name the exact action that failed → caches → the CA: publication events, schedule, and CDP settings → the offline root's CRL → what each relying party does with an unknown result. certutil, Get-WinEvent, wevtutil, Invoke-WebRequest, Test-NetConnection, Get-CACrlDistributionPoint, and Get-ItemProperty are the sources for that chain. Set the ignore flags everywhere, and you'll be turning them back off one app at a time, with the CRL still expired.

If certificates aren't arriving at all, start with Certificate Autoenrollment Not Working; its Step 9 is where this post picks up. If the failure is a Wi-Fi profile on Intune-managed devices, Intune Wi-Fi Profile Not Applying covers the profile side. This post covers "Windows clients, servers, or the CA itself fail to validate certificates from an AD CS hierarchy with 'the revocation server was offline' (0x80092013) or 'unable to check revocation' (0x80092012), and you need a read-first path before you turn off revocation checking, set ignore-offline flags, or republish over the top."

TL;DR

  • Read the chain before you touch the CA. certutil -verify -urlfetch on the failing certificate fetches every AIA and CDP URL in the chain and shows which one failed (Step 2). Microsoft's autoenrollment blog: "Verify that all certification authorities in the chain have valid CRL's published."
  • Check NextUpdate on every CRL in the chain. Microsoft's CAPI2 guide: "If the current date is later than the date in the nextUpdate field in the CRL, it means that the CRL has expired." The offline root's CRL is the usual suspect, because nothing publishes it on a schedule (Steps 3, 9).
  • Test from the context that fails. Microsoft's 0x80092013 article says to "verify that the CRL is downloadable from the system context". Services run as SYSTEM with WinHTTP proxy settings, not yours (Step 4).
  • Publishing isn't delivering. Microsoft: certutil -CRL "writes new CRL data according to the CA configuration; it doesn't prove that a web server, replication process, or external transfer makes the new files available." (Step 5).
  • A fixed CRL can still fail from cache. Microsoft's fix for the old-cache cause is certutil -setreg chain\ChainCacheResyncFiletime @now and certutil -urlcache * delete (Steps 7, 11).
  • Ignore-offline flags are a stopgap with an owner and an end date, not a fix. The CA flag, the NPS keys, and the curl and Git switches all weaken validation (Step 10, common false starts).

Where in the chain did it break?

Step What should be true Typical symptom when it breaks
1. Symptom captured Which app, error code, certificate, context, network, since when "Certificates are broken" with no certificate exported
2. Chain read certutil -verify -urlfetch run on the failing certificate, in the failing context Only the admin's browser was tested
3. CRL time-valid Every CRL in the chain is before its NextUpdate Root CRL expired over a weekend
4. CDP reachable The client can resolve and reach the first usable CDP URL as the identity that validates Works for the admin, fails as SYSTEM or over VPN
5. Location current HTTP and LDAP locations serve the CRL the CA last generated CA has a new CRL; the web server has last month's
6. CAPI2 read The failing action is named "Offline" with no detail
7. Caches The client uses the current CRL, not a cached old one Fixed on the server, still failing on clients
8. CA side CRLs publish on schedule to every location; CDP settings match what certificates carry Events 65/74 nobody reads; CDP URL changed last year
9. Offline root The root's CRL was renewed and distributed before NextUpdate The root has been off since its last CRL
10. Relying parties Each app's revocation policy is known and unchanged NPS keys flipped during the last outage and never flipped back
11. Change Only after Steps 1-10 agree: one link, one change Ignore flags everywhere, same expired CRL

Step 1: Capture the symptom (read-only)

Write these on the ticket first:

Fact Why it matters
Which app, and its exact error 0x80092013 and 0x80092012 are different results (table below); app wording varies, the code doesn't
Which certificate Export it (public part only, .cer) from the app's server, the client's store, or the CA. Step 2 needs the file
Which context validates it A service, NPS, the CA service, and scheduled tasks validate as SYSTEM or a service account; a browser validates as the user (our reading)
Which network LAN, VPN, Wi-Fi onboarding VLAN, internet. A CDP reachable from one isn't proof for another
Since when, and what changed A date matching a CRL's NextUpdate is the strongest lead; also CA renewal, web server or DNS change, proxy change, firewall change

The codes, each from Microsoft's Winerror.h page:

Code Name Microsoft's text Usually points at (our reading unless noted)
0x80092013 CRYPT_E_REVOCATION_OFFLINE "The revocation function was unable to check revocation because the revocation server was offline." A CRL that couldn't be retrieved or isn't valid (Steps 3-7)
0x80092012 CRYPT_E_NO_REVOCATION_CHECK "The revocation function was unable to check revocation for the certificate." No usable revocation information at all, often no CDP; common from curl and Git on Windows (Step 10)
0x80092010 CRYPT_E_REVOKED "The certificate is revoked." Revocation worked: the certificate is revoked. Don't clear caches to make it go away
0x80090358 SEC_E_REVOCATION_OFFLINE_KDC "The revocation status of the domain controller certificate used for smartcard authentication could not be determined." Smart card or certificate logon: the DC certificate's CRL (Step 10)

Then branch on what you see:

What you see What it usually means (our reading unless noted) Start at
Everything certificate-based failed at the same time One CRL high in the chain expired, usually the root's Steps 2, 3, 9
CA service won't start, Event 100 or 48 The CA can't check its own chain: usually the parent CA's CRL (Microsoft's Event 100 page) Steps 8, 9
Works on the LAN, fails over VPN or the internet The CDP URL is internal-only, or LDAP-only Step 4
Works for you, fails for the service SYSTEM proxy, or a CDP that needs authentication Step 4
Fails right after a CRL was republished Stale web copy, or cached old CRL Steps 5, 7
Base CRL fine, delta fails Delta not served (IIS double escaping), or not published Step 5
Pages slow, no error Revocation fetches timing out (SharePoint's 15-second example) Steps 4, 10

Step 2: Read the chain with certutil -verify -urlfetch (read-only)

This is the one test that answers most of the questions at once. Microsoft's autoenrollment blog: "The client does not have a valid certificate revocation list (CRL) from the issuing CA that it can use to check if a certificate has been revoked." Its test is to run certutil -v -verify -urlfetch against the issuing CA's certificate. Microsoft's CDP article uses the same test for any certificate: "From each representative network zone, validate the test certificate and retrieve its revocation data". Run it on the certificate that fails, on the machine that fails:

:: Read-only (writes one local file). Verify the chain and fetch every AIA and CDP URL in it (file names are examples)
certutil -v -verify -urlfetch C:\Temp\failing.cer > C:\Temp\verify-failing.txt

:: Read-only (writes one local file). The issuing CA's own certificate, from the CA (config string is an example)
certutil -config "CA01.contoso.com\Contoso Issuing CA" -ca.cert C:\Temp\issuing-ca.cer
certutil -verify -urlfetch C:\Temp\issuing-ca.cer
Enter fullscreen mode Exit fullscreen mode

Microsoft's description of -verify: "Verifies a certificate, certificate revocation list (CRL), or certificate chain." Microsoft's caution on running it: "it will use the security and proxy settings of the user that runs it". An admin session that passes doesn't clear the SYSTEM account; Step 4 has the SYSTEM form.

How to read the output (structure from the sample output a user posted on Microsoft Q&A, community; status wording is our reading):

Part of the output What it tells you
One CertContext[0][n] block per certificate [0][0] is the certificate you passed; the last one is the root
Certificate AIA / Certificate CDP under each block Each URL that block's certificate points at, with a status. Verified is good; anything else is the lead
Base CRL CDP Where the CRL says its delta CRL lives (empty if the CA doesn't publish deltas)
CRL nn: with ThisUpdate / NextUpdate The CRL that was used, and when it stops being valid
The last lines A pass shows a completed message; a failure names the error code (our reading)
# Read-only. Pull the lines that matter out of the saved output (pattern list is ours)
Select-String -Path C:\Temp\verify-failing.txt -Pattern 'CertContext|CDP|CRL \d|ThisUpdate|NextUpdate|Verified|Expired|Failed|0x8009' |
  Select-Object LineNumber, Line
Enter fullscreen mode Exit fullscreen mode
Finding What it means (our reading unless noted) Next
The root's block shows no CDP Normal. Microsoft's CAPI2 guide: a CDP is necessary "for any certificate except for a root CA certificate" The next block down
The issuing CA's block: CDP URL not Verified The root's CRL (it covers the issuing CA's certificate) is expired or unreachable Steps 3, 9
The leaf's block: CDP URL not Verified The issuing CA's CRL is expired or unreachable Steps 3-5
NextUpdate in the past on any CRL Expired. Republish it (Step 11) Step 3
First CDP URL fails, a later one is Verified Validation can still pass, but slowly; Microsoft says Windows clients try them in order Step 4
Passes for you, fails in the app Different context, proxy, or network Step 4
A block with no CDP and no OCSP that isn't the root Nothing to check against: 0x80092012 territory Step 8: the CA's CDP settings

Step 3: Which CRL, and is it still time-valid? (read-only)

Every CA in the chain except the root's own certificate needs a current CRL: the root's CRL covers the issuing CA's certificate, and the issuing CA's CRL covers the leaf. Microsoft's CAPI2 guide on what expired looks like: "If the current date is later than the date in the nextUpdate field in the CRL, it means that the CRL has expired." Its fix is one line: "To fix this error, publish a time-valid CRL on the server."

Read the CRLs the clients actually download, not only the CA's copy. Take the HTTP URL from Step 2's output:

# Read-only (writes local files). Download each CRL from its HTTP CDP and read its validity (URLs are examples)
$crls = @{
  'issuing' = 'http://pki.contoso.com/pki/ContosoIssuingCA.crl'
  'root'    = 'http://pki.contoso.com/pki/ContosoRootCA.crl'
}
foreach ($name in $crls.Keys) {
  Invoke-WebRequest -Uri $crls[$name] -OutFile "C:\Temp\$name.crl" -UseBasicParsing
  "== $name"
  certutil -dump "C:\Temp\$name.crl" | Select-String -Pattern 'ThisUpdate|NextUpdate'
}
Enter fullscreen mode Exit fullscreen mode

certutil -dump "Dumps the configuration information or files." certutil -URL opens the same check interactively: it "Verifies certificate or CRL URLs." On the CA, read what the CA itself generated and what it thinks it published:

# Read-only. On the CA: the CRL files the CA writes locally (default CertEnroll path) and their timestamps
Get-ChildItem -Path "$env:SystemRoot\System32\CertSrv\CertEnroll" -Filter *.crl |
  Select-Object Name, Length, LastWriteTime
Enter fullscreen mode Exit fullscreen mode
:: Read-only. On the CA: base and delta CRL publish status, then the CRL schedule values (Name* is certutil's prefix match)
certutil -CAInfo crlstatus
certutil -CAInfo deltacrlstatus
certutil -getreg ca\CRLPeriod*
certutil -getreg ca\CRLOverlap*
certutil -getreg ca\CRLDelta*
Enter fullscreen mode Exit fullscreen mode

certutil -CAInfo lists crlstatus as "CRL Publish Status" and deltacrlstatus as "Delta CRL Publish Status". The CRL period is how long each CRL is valid; the overlap is the head start clients get. Microsoft's two-tier PKI lab guide: "The overlap period for CRLs is the amount of time at the end of a published CRLs lifetime that a client can use to obtain a new CRL before the old CRL is considered unusable". And: "The default setting for this value is 10% of the CRL lifetime." Microsoft's CDP article keeps the two ideas apart: "A publishing interval and a CRL validity period are different settings."

LDAP CDPs live in AD. Read when each CDP object last changed (our form; whenChanged moves when a CRL is written there, our reading):

# Read-only. CRL distribution point objects in the configuration partition and when each last changed
$config = (Get-ADRootDSE).configurationNamingContext
Get-ADObject -SearchBase "CN=CDP,CN=Public Key Services,CN=Services,$config" `
  -Filter "objectClass -eq 'cRLDistributionPoint'" -Properties whenChanged |
  Select-Object DistinguishedName, whenChanged | Sort-Object whenChanged
Enter fullscreen mode Exit fullscreen mode

certutil -dsCRL "Displays DS CRLs." Add -config to point it at one CA (our reading of its options).

Finding What it means (our reading unless noted) Next
Root CRL past NextUpdate Nobody renewed the offline root's CRL Step 9
Issuing CRL past NextUpdate, CA running The CA isn't publishing, or publishes somewhere clients don't read Steps 5, 8
CertEnroll copy current, HTTP copy old Publication worked, delivery didn't Step 5
HTTP copy current, LDAP object old LDAP publication failing (Events 65/74) Step 8
All current, still failing Reachability, context, or cache Steps 4, 7
Overlap of zero or a few minutes Clients get no head start; any delay becomes an outage Step 11

Step 4: Can this client, in this context, reach the CDP? (read-only)

Microsoft's 0x80092013 article lists five reasons a CRL location is unreachable, including "System context proxy is applied but unreachable and requires user authentication." and "CRL Location requires authentication before access is granted." Microsoft's CDP article puts the test plainly: "Test the exact CDP URL from that client network."

Name and port first, from the failing machine:

# Read-only. The CDP host from Step 2: DNS answer, then TCP 80 (host name is an example)
$cdpHost = 'pki.contoso.com'
Resolve-DnsName -Name $cdpHost
Test-NetConnection -ComputerName $cdpHost -Port 80 -WarningAction SilentlyContinue |
  Select-Object ComputerName, RemoteAddress, TcpTestSucceeded
Enter fullscreen mode Exit fullscreen mode

Then the context. Services don't use your browser's proxy. Read the machine's WinHTTP proxy and the two hives Microsoft's article checks for the system context:

:: Read-only. WinHTTP proxy (what many services use), then the proxy values Microsoft's 0x80092013 article reads
netsh winhttp show proxy
reg query "HKU\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable
reg query "HKU\S-1-5-18\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer
reg query "HKU\S-1-5-18\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v AutoConfigURL
Enter fullscreen mode Exit fullscreen mode

Then repeat Step 2 as SYSTEM. Sysinternals PsExec's -s switch will "Run the remote process in the System account." Run locally, it does the same on this machine (our reading):

:: Read-only (writes one local file). Step 2's test as SYSTEM on the failing machine
psexec -s cmd /c "certutil -verify -urlfetch C:\Temp\failing.cer > C:\Temp\verify-system.txt"
Enter fullscreen mode Exit fullscreen mode

Then the URL list. Microsoft's PKI design page: "Remember that Windows clients always retrieve the list of URLs in sequential order until a valid CRL is retrieved." An LDAP URL first in the list costs a non-domain or off-network client a timeout before it reaches HTTP. The same page: "Use HTTP CDP locations to provide accessible CRL locations for clients running non-Windows operating systems." For Entra joined devices, Microsoft's Windows Hello for Business guide says the same: "Publish your certificate revocation list to a location that is available to Microsoft Entra joined devices, such as a web-based URL". Microsoft's CAPI2 guide rules one scheme out: "CryptoAPI does not support the FTP protocol for network retrieval."

Finding What it means (our reading unless noted) Next
Name doesn't resolve off-LAN Internal-only DNS name in the CDP Step 11: an HTTP name every zone can resolve, for new certificates (Step 8)
TCP 80 blocked from the VPN pool Firewall rule missing for the CDP host Network change for that one host and port
Passes as you, fails as SYSTEM WinHTTP or system-context proxy Step 11: proxy bypass for the CDP host
Only LDAP URLs in the CDP Non-domain, Entra joined, and off-network clients can't use them Step 8: add HTTP for new certificates
The proxy asks for credentials SYSTEM can't answer (Microsoft's cause) Proxy exception for the CDP host

Step 5: Does the web or LDAP location serve the current file? (read-only)

The CA writing a CRL and a client getting it are separate jobs. Microsoft: "The CA's local generation result doesn't validate the delivery chain." Its monitoring advice: "At each HTTP endpoint, verify that the expected CRL file is current, complete, and readable by the intended clients." Compare the CA's file with what the web server hands out:

# Read-only. Same bytes on the CA and at the HTTP CDP? (file names are examples; C:\Temp\issuing.crl is Step 3's download)
Get-FileHash -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\ContosoIssuingCA.crl", 'C:\Temp\issuing.crl' -Algorithm SHA256 |
  Select-Object Hash, Path
Enter fullscreen mode Exit fullscreen mode

Different hashes with a current CA file means the copy job, the share, or the replication between web servers didn't run (our reading). Microsoft's lab guide grants Cert Publishers modify rights on the web server's pki folder so the CA can write there; its 0x80092013 article adds "check File System Permissions on the CRL and Delta CRL files."

Delta CRLs and IIS. A delta CRL's file name carries a +. Microsoft's lab guide: "Allowing double escaping is needed if you are publishing Delta CRLs to IIS because the Delta CRL file contains a + symbol." IIS's request filtering reference: "If set to true, request filtering will allow URLs with doubly-escaped characters." and "The default value is false." Read the setting on the CDP's site or folder:

# Read-only. On the IIS server: double escaping on the CDP folder (site and folder names are examples)
Import-Module WebAdministration
Get-WebConfigurationProperty -PSPath 'IIS:\Sites\Default Web Site\pki' -Filter system.webServer/security/requestFiltering -Name allowDoubleEscaping
Enter fullscreen mode Exit fullscreen mode

Microsoft's CDP article also asks for "the .crl MIME mapping required by your web-server policy" and for checking "the actual HTTP response after deployment". Step 3's Invoke-WebRequest download is that check for the base CRL; repeat it with the delta file name (the + one) from Step 2's Base CRL CDP line.

Finding What it means (our reading unless noted) Next
Hashes differ The web copy is stale Step 11: the copy or replication job
Base downloads, delta returns 404 Double escaping off, or the delta was never copied Step 11: double escaping (Microsoft's lab form)
401 or 403 from the CDP URL Anonymous read missing on the folder or site Folder permissions
CRL downloads, certutil -dump can't parse it The server returns something else (an error page, a captive portal) The response itself

Step 6: Turn on CAPI2 logging and read the action that failed (read-only, one log switch)

When -urlfetch passes and the app still fails, the CAPI2 log shows what the app's own validation did. Microsoft's CAPI2 guide on why the app's message isn't enough: the API "was designed to return an error code and string", so many network problems reach the app as revocation offline. Enable the log, make it bigger, reproduce once, export, and turn it off. Microsoft: "The default size for the event log is 1 MB." It recommends "at least 4 MB to capture relevant events".

:: Low-risk. Enable CAPI2 logging at 4 MB, reproduce the failure, export, then disable (Microsoft's wevtutil forms)
wevtutil sl Microsoft-Windows-CAPI2/Operational /ms:4194304
wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true
:: reproduce the failure now, then:
wevtutil epl Microsoft-Windows-CAPI2/Operational C:\Temp\capi2.evtx
wevtutil sl Microsoft-Windows-CAPI2/Operational /e:false
Enter fullscreen mode Exit fullscreen mode
# Read-only. CAPI2 errors, then the ones whose XML names a known revocation action (action list from Microsoft's guide; the filter is ours)
$capi = Get-WinEvent -LogName 'Microsoft-Windows-CAPI2/Operational' -MaxEvents 2000 -ErrorAction SilentlyContinue
$capi | Where-Object Level -eq 2 | Select-Object TimeCreated, Id, TaskDisplayName | Format-Table -AutoSize
$capi | Where-Object { $_.ToXml() -match 'CheckTimeValidity|CanRetrieveFromNetwork|CanCheckServerForUpdatedCrl|BadProxy|GetCrlOrOcspUrls|IsCrlSignatureValid|2EE7|2EFD' } |
  Select-Object TimeCreated, Id, TaskDisplayName
Enter fullscreen mode Exit fullscreen mode

Microsoft's guide names the events to open: results are in CertVerifyRevocation, and "To find the cause, you need to look at the" CertRejectedRevocationInfo and CryptRetrieveObjectByUrlWire events nested under it. In CertGetCertificateChain, the status flags CERT_TRUST_REVOCATION_STATUS_UNKNOWN plus CERT_TRUST_IS_OFFLINE_REVOCATION mean the revocation information couldn't be retrieved or was invalid. The actions, each from Microsoft's guide:

Action or code in the event Microsoft's meaning Link in the chain
CheckTimeValidity (CertRejectedRevocationInfo) The CRL is expired: now is past its nextUpdate Steps 3, 9
IsCrlSignatureValid "If the signature of the CRL cannot be verified" The CRL file at that location is wrong or corrupt (Step 5)
GetCrlOrOcspUrls No CDP and no OCSP URL in the certificate Step 8
CanRetrieveFromNetwork Back-off: "CAPI2 avoids contacting the server too often for repeated offline responses." Wait for EarliestOnlineTime, or Step 7
BadProxy (CryptRetrieveObjectByUrlWire) The proxy is wrong; usually the first in the list Step 4
2EE7 ERROR_WINHTTP_NAME_NOT_RESOLVED DNS for the CDP or proxy host
2EFD ERROR_WINHTTP_CANNOT_CONNECT The CDP host's port, or the host is down

Microsoft's summary for the network rows: "fix the underlying component (such as network, proxy settings, Web server, or LDAP) that is causing failure."


Step 7: Is the client using a cached old CRL? (read-only)

Clients keep CRLs. Microsoft's 0x80092013 article: "The CRL is retrieved by clients who can then cache the CRL (based on the configured lifetime of the CRL)". So a CRL you republished an hour ago doesn't help a client that cached the old one, and its fourth cause is exactly that: "The CRL is reachable but the client is picking from an old cache." Read the cache before you clear it:

:: Read-only. Cached CRL URLs for the current user's context (certutil's CRL filter), saved for the ticket
certutil -urlcache CRL > C:\Temp\urlcache-crl.txt
Enter fullscreen mode Exit fullscreen mode

Microsoft's syntax note for -URLCache: delete "deletes relevant URLs from the current user's local cache." Run the read in the same context as the failure: as the user for a user app, and as SYSTEM (Step 4's PsExec form) for a service (our reading).

CAPI2's back-off is a second kind of cache. After repeated offline results it stops asking; the CanRetrieveFromNetwork event gives the EarliestOnlineTime it will try again (Step 6).

Finding What it means (our reading unless noted) Next
Cached CRL for the failing URL, and the server copy is newer Client still holds the old one Step 11: Microsoft's two cache commands, after the server side is fixed
No cached entry, still failing Not a cache problem Steps 4, 5
CanRetrieveFromNetwork in CAPI2 Back-off after repeated failures Fix the URL first, then Step 11

Step 8: The CA side: publication, schedule, and CDP settings (read-only)

Did the CA publish? Microsoft's AD CS event list gives the CRL publication events in the Application log:

Event Microsoft's text (event list for AD CS) Means (our reading)
48 "Revocation status for a certificate in the chain for CA certificate %3 for %1 could not be verified because a server is currently unavailable." The CA can't check its own chain (usually the parent's CRL)
49 "could not be verified because no information is available describing how to check the revocation status." A certificate in the CA's chain has no usable CDP
65 / 66 "Active Directory Certificate Services could not publish a Base CRL for key %1 to the following location: %2." (66: Delta) A publication location refused the write: path, share, or LDAP rights
67 "made %1 attempts to publish a CRL and will stop publishing attempts until the next CRL is generated." Retries ran out; that location stays stale until the next CRL
74 / 75 The same base / delta failure "to the following location on server %4" Same, for a named server
100 "Active Directory Certificate Services did not start: Could not load or verify the current CA certificate." (Microsoft's Event 100 page) The CA won't start; the CRL for a CA above it is the common cause
# Read-only. On the CA: chain, publication, and startup events, last 30 days (IDs from Microsoft's AD CS event list)
Get-WinEvent -FilterHashtable @{
  LogName = 'Application'; ProviderName = 'Microsoft-Windows-CertificationAuthority'
  Id = 48, 49, 65, 66, 67, 74, 75, 100; StartTime = (Get-Date).AddDays(-30)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, Message | Format-List

# Read-only. Is Certificate Services running?
Get-Service -Name CertSvc | Select-Object Name, Status, StartType
Enter fullscreen mode Exit fullscreen mode

A Microsoft Q&A post shows what a failed LDAP publish looks like in practice: "could not publish a Base CRL for key 0 to the following location: ldap:///" with a logon failure code (community; the location is redacted in the post).

Where does the CA publish, and what do certificates carry? Microsoft: the CA's CDP configuration "represents both publication and retrieval settings". Read both:

# Read-only. On the CA: every CDP and AIA entry with its flags (Microsoft's lab guide uses the same Format-List form)
Get-CACrlDistributionPoint | Format-List
Get-CAAuthorityInformationAccess | Format-List
Enter fullscreen mode Exit fullscreen mode

Microsoft's cmdlet page lists the flags on each CDP entry: PublishToServer, PublishDeltaToServer, AddToCertificateCdp, AddToFreshestCrl, AddToCrlCdp, AddToCrlIdp, and Uri. PublishToServer is where the CA writes; AddToCertificateCdp is what clients get told. A URL with AddToCertificateCdp and nothing that copies the file there is a CDP that goes stale on its own (our reading).

The trap with CDP changes: "Changing a CDP URL affects only certificates issued after the change." Microsoft's design page says the same: "Modifying the URL for a CRL distribution point only affects newly issued certificates." Certificates issued before a change still point at the old URL. Microsoft's troubleshooting table for that case: "Keep the old CDP online and restore current CRL content there. A CDP configuration change doesn't repair old certificates."

The CA's own startup. Microsoft's Event 100 page: AD CS "startup can fail if there are problems with availability, validity, and chain validation for the CA certificate." Its resolution includes: "If a CRL for a CA in the chain has expired, generate new base and delta CRLs on this CA and copy them to the required locations." Microsoft's lab guide is blunt about the subordinate: if it can't find its parent's CRLs, the service "will fail to start on the subordinate CA". For the issuing CA, the parent is usually the offline root, so Step 9 is the fix. Read the CA's current ignore flags before anyone changes them:

:: Read-only. On the CA: CRLFlags as set now (a stopgap someone set last year shows up here)
certutil -getreg ca\CRLFlags
Enter fullscreen mode Exit fullscreen mode
Finding What it means (our reading unless noted) Next
Events 65/74 for an LDAP location The CA can't write its CDP object Rights on the CDP container; Step 11
Events 65/74 for a file or UNC location Share, NTFS, or path problem The share and Cert Publishers rights (Step 5)
Event 100 or 48 at startup The parent CA's CRL is expired or unreachable from the CA Step 9
CRLF_REVCHECK_IGNORE_OFFLINE already set A stopgap from an earlier outage, hiding offline results on the CA Fix the CRL, then remove it (Step 11)
CDP URL in certificates differs from where the CA publishes Old certificates point at a location nobody updates Keep the old location current (Microsoft)
No HTTP URL with AddToCertificateCdp Off-network and non-Windows clients can't fetch Step 11: staged CDP add for new certificates

Step 9: The offline root's CRL (read-only)

The offline root is the one CA nobody turns on, so its CRL is the one that expires. Microsoft's CDP article: "For an offline root CA, avoid delta CRLs unless a documented requirement justifies them. Use a scheduled, witnessed process to publish a new base CRL, transfer it to every retrieval endpoint, and verify it from the networks that depend on the root." Microsoft's lab guide sets the root up that way in its CAPolicy.inf: CRLPeriod=weeks, CRLPeriodUnits=26, CRLDeltaPeriodUnits=0, and explains: "Setting the CRLDeltaPeriodUnits=0 in the CAPolicy.inf disables Delta CRL publishing, which is the appropriate setting for an offline Root CA."

Read the root CRL's dates without powering on the root. Step 3's download of the root CRL gives NextUpdate; so does the issuing CA's block in Step 2's output. Then read the copy the issuing CA keeps locally. Microsoft's lab guide adds the root CRL to the subordinate's local Root store during setup; -store lists that store's contents (our reading that the CRL entries show there):

:: Read-only. On the issuing CA: CRLs in the local machine Root store (where the lab guide's addstore puts the root CRL)
certutil -store Root > C:\Temp\root-store.txt
Enter fullscreen mode Exit fullscreen mode

A community answer on Microsoft Q&A notes a stale root CRL in that store can keep a service failing after the HTTP copy is fixed. The same answer adds that "Things like MSCEP" (NDES) read it from there; if Intune SCEP broke at the same time, Intune SCEP Certificate Profile Failed covers the NDES side.

Two clock checks before you blame the CRL (our reading): the root's clock sets ThisUpdate and NextUpdate on the CRL it signs, and a root VM that boots once a year can come up with the wrong time. A client clock that's off by days can call a valid CRL expired, too. Windows Time Not Syncing With Domain Controller covers the domain clients' side.

Finding What it means (our reading unless noted) Next
Root CRL past NextUpdate Everything under the root fails revocation Step 11: the offline root sequence
Root CRL valid for days, not months The CRL period is short for a CA that's off Step 11: raise it on the root, then publish
HTTP root CRL current, issuing CA's store copy old The CA validates with the old copy Step 11: addstore, then cache resync
Root CRL's ThisUpdate is in the future or far past The root's clock was wrong when it signed Fix the root's time, then republish

Step 10: What each relying party does with "unknown" (read-only)

The same expired CRL looks different in each app. Read each app's policy before you change it, so you know what you'd be turning off:

NPS (802.1X Wi-Fi and VPN, EAP-TLS). Microsoft's NPS page lists four DWORDs under HKLM\SYSTEM\CurrentControlSet\Services\RasMan\PPP\EAP\13, and "The following keys are set to 0 by default." With IgnoreRevocationOffline at 0: "When the NPS can't connect to a server that stores a revocation list, the certificate fails the revocation check and authentication fails." Read them on every NPS server:

# Read-only. On each NPS server: the EAP-TLS revocation keys (missing = default 0)
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\PPP\EAP\13' -ErrorAction SilentlyContinue |
  Select-Object IgnoreNoRevocationCheck, IgnoreRevocationOffline, NoRevocationCheck, NoRootRevocationCheck
Enter fullscreen mode Exit fullscreen mode

Microsoft's 802.1X troubleshooting page puts revocation in its list of usual causes: "Examples include invalid certificate, expiration, chain verification failure, and revocation check failure."

Smart card and certificate logon. Microsoft's smart card article: "The revocation check must succeed from both the client and the domain controller." It also requires that "Every CA Certificate except the root CA in the certificate chain contains a valid CDP extension in the certificate." The client-side code for the DC's certificate is 0x80090358 (Step 1). Microsoft's DC certificate requirements: "The certificate must have a CRL distribution-point extension that points to a valid certificate revocation list (CRL)." Run Step 2 on a DC's certificate from a client, and on the client's certificate from a DC.

DirectAccess. Microsoft's article is the 0x80092013 source above; its read is netsh interface http show interface on the client.

SharePoint and other apps that call out to public CRLs. Microsoft's SharePoint article describes the slow version: "If the online CRL server cannot be reached from the SharePoint server for some reason, the operation times out after 15 seconds by default." No error, just slow pages (our reading of that article for internal apps).

curl and Git on Windows. Both use Schannel, Windows' own validation, so an internal or proxy-inspected certificate whose CDP isn't reachable fails there too, often as 0x80092012 (our reading). curl's manual describes --ssl-revoke-best-effort: "Ignore certificate revocation checks when they failed due to missing/offline distribution points for the revocation check lists." and warns about --ssl-no-revoke: "WARNING: this option loosens the SSL security, and by using this flag you ask for exactly that." Git's http.schannelCheckRevoke: "Only necessary to disable this if Git consistently errors and the message is about checking the revocation status of a certificate." Run Step 2 on the server certificate first; if a TLS-inspecting proxy re-signs traffic, the CDP that fails is the proxy CA's (our reading).

Finding What it means (our reading unless noted) Next
NPS keys all 0 or missing Default: NPS fails closed when a CRL is unavailable Fix the CRL (Steps 3-9)
NoRevocationCheck = 1 already Revocation is off on that NPS server; revoked certificates connect Find who set it and why, then plan to remove it
Smart card logon fails, password logon works DC or client can't check the other's chain Step 2 in both directions
Only curl / Git fail Schannel can't reach the CDP of that certificate's issuer Step 2 on the server certificate; Step 4

Common false starts (read these before you change anything)

False start Why it hurts Read-first alternative
CRLF_REVCHECK_IGNORE_OFFLINE on the CA, left on Community answers on Microsoft Q&A use it to get a CA started; left on, the CA stops caring whether its chain's CRLs are reachable (our view) Step 9's root CRL first; if you must use it, Step 11's time-boxed form
NoRevocationCheck = 1 on NPS Microsoft: with it enabled, NPS "prevents EAP-TLS from performing a revocation check of the certificate of the client." A revoked laptop certificate connects Fix the CRL; if you need a bridge, Step 11
Revocation off in the VPN or app profile Every client now needs a second change to undo it (our view) Step 10 for that app
Changing the CDP URL on the CA to repair old certificates Microsoft: "Changing a CDP URL affects only certificates issued after the change." Restore current content at the old URL (Step 8)
Republishing on the CA and declaring it fixed Publishing isn't delivering; clients may hold the old one Steps 5, 7
Renewing the CA certificate A new CA certificate doesn't renew the parent's CRL that failed (our reading) Step 9
certutil -urlcache * delete on every machine, repeatedly Clears the symptom for an hour if the server copy is still old (our reading) Fix the server copy, then clear once
Delta CRLs on the offline root Microsoft: "Avoid publishing delta CRLs on offline root CAs." Each one means powering on the root Base CRL only on the root (Step 9)

Step 11: Change one link, then prove it (only after the read)

Pick the one change Steps 1-10 point at. Save the current state first:

# Low-risk write. Save the CA configuration, CDP/AIA settings, and current CRL files (our habit; run on the CA)
New-Item -Path C:\Temp\pki-before -ItemType Directory -Force | Out-Null
reg export 'HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration' C:\Temp\pki-before\certsvc-config.reg /y
Get-CACrlDistributionPoint | Format-List > C:\Temp\pki-before\cdp.txt
Get-CAAuthorityInformationAccess | Format-List > C:\Temp\pki-before\aia.txt
Copy-Item -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\*.crl" -Destination C:\Temp\pki-before
Enter fullscreen mode Exit fullscreen mode

Expired issuing CA CRL (Step 3). On the CA, Microsoft's scripted form:

:: Change, on the issuing CA. Publish new base (and delta, if configured) CRLs, then read the status back
certutil -config "CA01.contoso.com\Contoso Issuing CA" -CRL
certutil -CAInfo crlstatus
Enter fullscreen mode Exit fullscreen mode

Microsoft: certutil -CRL "Publishes new certificate revocation lists (CRLs) or delta CRLs." Then deliver it. If no job copies the file, copy it to every HTTP location yourself (Microsoft's server-certificate guide uses a plain copy to the web server's share), and rerun Step 5's hash check:

# Change. Copy the CA's CRLs to the web server's pki share (share name is an example; Microsoft's guide uses \\WEB1\pki)
Copy-Item -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\*.crl" -Destination '\\WEB01\pki' -WhatIf
Enter fullscreen mode Exit fullscreen mode

Expired offline root CRL (Step 9). Microsoft's Event 100 page and lab guide, in order (the order and the dry-run habits are ours):

  1. Power on the root. Check its clock before anything else (Step 9).
  2. If the CRL period is too short for how often you'll do this, change it first. Microsoft's lab guide sets the root's values with certutil -setreg, restarts the service, and publishes; one example:
# Change, on the offline root (elevated PowerShell). Longer CRL validity, 12-hour overlap, restart, publish (Microsoft's lab form; values are examples)
certutil -setreg CA\CRLPeriodUnits 26
certutil -setreg CA\CRLPeriod "Weeks"
certutil -setreg CA\CRLOverlapPeriodUnits 12
certutil -setreg CA\CRLOverlapPeriod "Hours"
Restart-Service -Name CertSvc
certutil -crl
Enter fullscreen mode Exit fullscreen mode
  1. Copy the new CRL from %windir%\System32\CertSrv\CertEnroll to removable media, then power the root off.
  2. On a domain-joined machine, publish it to AD. -dspublish "Publishes a certificate or certificate revocation list (CRL) to Active Directory." and "Use -f to create a new DS object." Its container argument "is the DS CDP container CN, usually the CA machine name."
  3. Copy it to every HTTP CDP location, and add it to the issuing CA's local store the way Microsoft's lab guide does.
  4. Rerun Step 2 on the issuing CA's certificate, then start the issuing CA if it was down.
:: Change, on a domain member (the issuing CA works). Root CA machine name ORCA1 and file names are examples
certutil -f -dspublish C:\Temp\ContosoRootCA.crl ORCA1
copy C:\Temp\ContosoRootCA.crl \\WEB01\pki\
certutil -addstore -f root C:\Temp\ContosoRootCA.crl
certutil -verify -urlfetch C:\Temp\issuing-ca.cer
Enter fullscreen mode Exit fullscreen mode

Microsoft's lab guide on that last store step: "The second and third commands place the root CA certificate and CRL into the local store of APP1." Then put the next NextUpdate, minus the overlap, on a calendar with an owner. Microsoft: "Alert before a CRL reaches its next-update boundary."

A CA that won't start while you fix the root (Step 8). The fix is the parent's CRL. If issuance has to resume first, community answers on Microsoft Q&A use the ignore-offline flag, then remove it once the CRL is valid (the accepted answer also resyncs the chain cache before removing it). Treat it as a time-boxed exception with an owner (our view):

# Change, on the issuing CA (stopgap, elevated PowerShell). Read, set, restart; flag form from Microsoft Q&A community answers
certutil -getreg ca\CRLFlags
certutil -setreg ca\CRLFlags +CRLF_REVCHECK_IGNORE_OFFLINE
Restart-Service -Name CertSvc

# After the root CRL is fixed and Step 2 passes on the CA: resync the chain cache, remove the flag, restart
# ('@now' is quoted because PowerShell would read a bare @now as splatting)
certutil -setreg chain\ChainCacheResyncFiletime '@now'
certutil -setreg ca\CRLFlags -CRLF_REVCHECK_IGNORE_OFFLINE
Restart-Service -Name CertSvc
Enter fullscreen mode Exit fullscreen mode

Unreachable CDP (Step 4). One change for the one cause: a DNS record, a firewall rule from that network to the CDP host on TCP 80, or a proxy bypass for the CDP host so SYSTEM doesn't need proxy credentials. Then rerun Step 4 from that network and context.

Stale web copy or delta 404 (Step 5). Fix the copy job first. For deltas on IIS, Microsoft's 0x80092013 article gives the double-escaping setting; dry-run it on the CDP folder only:

# Change, on the IIS server. Allow double escaping on the CDP folder only (site and folder are examples; Microsoft's KB form plus -WhatIf)
Set-WebConfiguration -Filter system.webServer/security/requestFiltering -PSPath 'IIS:\Sites\Default Web Site\pki' -Value @{ allowDoubleEscaping = $true } -WhatIf
Enter fullscreen mode Exit fullscreen mode

Cached old CRL (Step 7). Only after the server copy is current. Microsoft's two commands, from the 0x80092013 article and the NPS page ("To manually update the CRL on your NPS server, run these commands in the command prompt or PowerShell"), in the context that fails:

:: Change (local caches only). Clear cached URLs and force chain revalidation; run as the failing context (SYSTEM via psexec -s for services)
certutil -urlcache * delete
certutil -setreg chain\ChainCacheResyncFiletime @now
Enter fullscreen mode Exit fullscreen mode

CDP changes for new certificates (Step 8). Microsoft's article is the procedure: "Use a staged transition. Add and validate the new endpoint before you remove an old one." Add-CACrlDistributionPoint "prompts for confirmation". For removal with Remove-CACrlDistributionPoint, use -WhatIf and the matching flags: "If you omit the flags, the cmdlet removes every distribution point that matches the URI."

# Change, on the CA. Add an HTTP URL to the CDP of newly issued certificates (Microsoft's example; prompts unless -Force)
Add-CACrlDistributionPoint -Uri "http://pki.contoso.com/pki/<CAName><CRLNameSuffix><DeltaCRLAllowed>.crl" -AddToCertificateCdp -AddToFreshestCrl
Enter fullscreen mode Exit fullscreen mode

Restart the CA if the cmdlet says so, publish new CRLs, issue one test certificate, and run Step 2 on it.

A bridge on NPS while the CRL is fixed (Step 10). Microsoft documents IgnoreRevocationOffline: "When enabled, the NPS allows EAP-TLS clients to connect even when a server that stores a CRL isn't available on the network". It's narrower than NoRevocationCheck: a certificate that's on a CRL NPS can read is still rejected (our reading). If you use it, use it on purpose, write down who owns setting it back to 0, and do it on every NPS server the same way (our view).

Then prove it. Microsoft's validation step: "Confirm that the output shows a successful retrieval of the base CRL and, when you use delta CRLs, of the associated delta CRL." From each network zone, as the user and as SYSTEM:

:: Read-only (writes local files). Step 2 again from each zone and context; then confirm with the app itself
certutil -verify -urlfetch C:\Temp\failing.cer > C:\Temp\verify-after.txt
psexec -s cmd /c "certutil -verify -urlfetch C:\Temp\failing.cer > C:\Temp\verify-after-system.txt"
Enter fullscreen mode Exit fullscreen mode

Then the thing the user waited for: one laptop on the 802.1X SSID, one VPN connection, one smart card logon. Turn CAPI2 logging off if it's still on (Step 6), and remove any stopgap flag you set (our habit).


When a change is justified

Finding Prefer Avoid
Root CRL expired Root on, certutil -crl, publish to AD and HTTP, store copy on the issuing CA Ignore flags on every relying party
Issuing CRL stale at HTTP Fix the copy job; hash-check the web copy Republishing again and again on the CA
CDP unreachable from one zone DNS, firewall, or proxy bypass for that host Turning off revocation for that zone's clients
Client still on the old CRL Clear its cache once, after the server is current Scheduled cache wipes on every machine
CA down during the root fix Time-boxed CRLF_REVCHECK_IGNORE_OFFLINE, then remove it Leaving it set after the CRL is fixed
Old certificates with a dead URL Keep that URL serving current CRLs Changing the CA's CDP and expecting old certificates to follow

FAQ

What does "the revocation server was offline" mean?
A client building a certificate chain couldn't get valid revocation information for one of the certificates: usually an expired CRL, a CDP URL it can't reach in its context, a CRL that was never published, or an old cached copy. Those are the four causes in Microsoft's 0x80092013 article. The error code is 0x80092013, CRYPT_E_REVOCATION_OFFLINE.

How do I find which CRL is expired?
Run certutil -verify -urlfetch on the failing certificate from the failing machine, then read NextUpdate on each CRL it fetched. Download the CRL from its HTTP URL and run certutil -dump on it to confirm. The offline root's CRL is the first one to check.

How do I renew an offline root CA's CRL?
Power on the root, run certutil -crl, copy the new CRL off on removable media, publish it to AD with certutil -f -dspublish, copy it to every HTTP CDP location, and add it to the issuing CA's store with certutil -addstore -f root. Then verify from the issuing CA with certutil -verify -urlfetch, and set a reminder before the next NextUpdate.

Why does it still fail after I published a new CRL?
Publishing on the CA doesn't copy the file to the web server, and clients keep cached CRLs. Compare the web copy's hash with the CA's file, then clear the failing context's cache with certutil -urlcache * delete and certutil -setreg chain\ChainCacheResyncFiletime @now.

What's the difference between 0x80092012 and 0x80092013?
0x80092013 is "revocation server was offline": revocation information exists but couldn't be retrieved or isn't valid. 0x80092012 is "unable to check revocation for the certificate", which is common when a certificate has no usable CDP at all, or from curl and Git on Windows. Both start with Step 2.


Related posts

Free checklist

An ignore-revocation flag pushed to every NPS server before one laptop was tested against a fixed CRL is a change nobody tested where the validation happens. So is a Conditional Access policy enforced before anyone read the report-only results. The free Conditional Access pre-enforcement checklist keeps break-glass accounts, pilot groups, and report-only review on one page:

👉 https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=devto&utm_medium=article&utm_campaign=aps_first_sale&utm_content=revocation-server-offline-crl-expired

If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the Intune & M365 Admin Starter Pack pairs with this triage: https://cashflow4375.gumroad.com/l/joonf?utm_source=devto&utm_medium=article&utm_campaign=aps_first_sale&utm_content=revocation-server-offline-crl-expired


Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks: certutil -verify -urlfetch, -ca.cert, -dump, -URL, -CAInfo, -getreg, -dsCRL, -urlcache CRL, -store, Select-String, Invoke-WebRequest, Get-ChildItem, Get-ADRootDSE, Get-ADObject, Resolve-DnsName, Test-NetConnection, netsh winhttp show proxy, reg query, Get-FileHash, Get-WebConfigurationProperty, Get-WinEvent, Get-Service, Get-CACrlDistributionPoint, Get-CAAuthorityInformationAccess, and Get-ItemProperty. Output redirection, downloads, wevtutil epl, and reg export write local files; psexec -s runs the same reads as SYSTEM. CAPI2 logging, certutil -CRL, CRL period and overlap values, -dspublish, -addstore, file copies to CDP locations, CRLFlags, Restart-Service CertSvc, cache clears, IIS double escaping, proxy and firewall changes, CDP changes, and NPS registry keys are deliberate changes. Change one link at a time, test from each network zone and context, and check current Microsoft Learn for version-specific behavior.

Top comments (0)