Server SSL | SChannel Client Certificate Revocation

Setting SSLOptions.VerifyCertificate to True makes the server ask each incoming client for a certificate and validate the chain it presents. That validation establishes that the client chain is trusted. It says nothing about whether the client certificate has been revoked since it was issued.

That gap matters wherever the certificate is the credential: a laptop is lost or an employee leaves, the certificate issued to them is revoked, and until the day it expires it still opens a session on your server. Revocation is looked up only when it is asked for, through SSLOptions.SChannel_Options.Revocation.

The revocation settings are read while a client certificate is being validated, so they do nothing unless SSLOptions.VerifyCertificate is True, which is what makes the server request a client certificate in the first place. Whether a client that presents no certificate at all is refused or let through is a separate decision, taken by SSLOptions.VerifyCertificate_Options.FailIfNoCertificate.


oServer.SSL := True;
oServer.SSLOptions.IOHandler := iohSChannel;
oServer.SSLOptions.VerifyCertificate := True;
oServer.SSLOptions.VerifyCertificate_Options.FailIfNoCertificate := True;
oServer.SSLOptions.SChannel_Options.Revocation.Check := scrcChainExcludeRoot;

The status is retrieved while the certificate chain is built, so the CRL distribution point or OCSP responder named by the client certificate is contacted during the handshake. That is why the check is off by default: it puts an outbound request in the path of every connection the server accepts.

Check

Selects how much of the client chain has its revocation status looked up.

scrcDisabled Default. No revocation status is retrieved and none is required.
scrcEndCertificate Only the client certificate itself is checked. The cheapest option, one lookup per connection.
scrcChainExcludeRoot The client certificate and the intermediates that issued it are checked, the root is not. The usual choice, because the root of the CA that issues your client certificates rarely publishes a CRL for itself.
scrcChain The whole chain including the root.

Timeout

Milliseconds allowed for the whole revocation retrieval, not per URL, so a responder that does not answer cannot stall the handshake while the server waits on it. Default 5000. A value of zero leaves the Windows default in force, which can be considerably longer.

CacheOnly

When True, only revocation data Windows has already cached is used and the handshake never goes to the network, so an incoming connection is never delayed by a responder. A cold cache then leaves the status unknown, which IgnoreNoRevocationCheck decides. Default False.

IgnoreRevocationOffline

Decides what happens when the responder could not be reached. Default True, accept the client certificate, which is the soft-fail behaviour. Set it to False to reject instead. Rejecting is stricter, but it makes every new connection depend on the responder being reachable from the server, so a responder outage becomes an outage for all of your clients.

IgnoreNoRevocationCheck

Decides what happens when the client certificate publishes no CRL distribution point and no OCSP responder at all, so its status cannot be established. Default True, accept it. Certificates issued by an internal CA frequently carry no revocation information, so set this to False only once you know that every certificate you hand to a client publishes one.

Neither Ignore property ever accepts a certificate that came back confirmed revoked. That outcome is always rejected and the handshake fails.

Reported statuses

When the chain check refuses a client, the reason is one of:

ecCryptRevoked The client certificate has been revoked by its issuer. Always rejected.
ecCryptRevocationOffline The responder could not be reached. Only rejected when IgnoreRevocationOffline is False.
ecCryptNoRevocationCheck The client certificate publishes no revocation information. Only rejected when IgnoreNoRevocationCheck is False.

Secure defaults

Setting SSLOptions.Preset to tlspSecureDefaults enables the check as scrcChainExcludeRoot on the server, matching what the same preset already applied on the client, and keeps the two soft-fail settings, so a client that used to be admitted is still admitted. A scope you chose yourself before applying the preset is left untouched.


oServer.SSLOptions.Preset := tlspSecureDefaults;

Notes