6 CRLs and Revocation
Certificates must sometimes be revoked. Unfortunately this means that the elegant chain-of-trust model breaks down, since the information you need to determine whether a certificate is trustworthy no longer depends on just the certificate and whether the issuer is trustworthy, but now on a third piece of data - whether the certificate has been revoked. These are managed in two ways in OpenSSL: CRLs and OCSP. SWI-Prolog supports CRLs only. (Typically OCSP responders are configured in such a way as to just consult CRLs anyway. This gives the illusion of up-to-the-minute revocation information because OCSP is an interactive, online, real-time protocol. However the information provided can still be several weeks out of date!)To do CRL checking, pass require_crl(true) as an option to the ssl_context/3 (or http_open/3) option list. If you do this, a certificate will not be validated unless it can be checked for on a revocation list. There are two options for this:
First, you can pass a list of filenames in as the option crl/1. If the CRL corresponds to an issuer in the chain, and the issued certificate is not on the CRL, then it is assumed to not be revoked. Note that this does NOT prove the certificate is actually trustworthy - the CRL you pass may be out of date! This is quite awkward to get right, since you do not necessarily know in advance what the chain of certificates the other party will present are, so you cannot reasonably be expected to know which CRLs to pass in.
Secondly, you can handle the CRL checking in the cert_verify_hook when the Error is bound to unknown_crl. At this point you can obtain the issuer certificate (also given in the hook), find the CRL distribution point on it (the crl/1 argument), try downloading the CRL (the URL can have literally any protocol, most commonly HTTP and LDAP, but theoretically anything else, too, including the possibility that the certificate has no CRL distribution point given, and you are expected to obtain the CRL by email, fax, or telegraph. Therefore how to actually obtain a CRL is out of scope of this document), load it using load_crl/2, then check to see whether the certificate currently under scrutiny appears in the list of revocations. It is up to the application to determine what to do if the CRL cannot be obtained - either because the protocol to obtain it is not supported or because the place you are obtaining it from is not responding. Just because the CRL server is not responding does not mean that your certificate is safe, of course - it has been suggested that an ideal way to extend the life of a stolen certificate key would be to force a denial of service of the CRL server.
6.1 Disabling certificate checking
In some cases clients are not really interested in host validation of
the peer and whether or not the certificate can be trusted. In these
cases the client can pass cert_verify_hook(cert_accept_any)
,
calling cert_accept_any/5
which accepts any certificate. Note that this will accept literally ANY
certificate presented - including ones which have expired, have been
revoked, and have forged signatures. This is probably not a good idea!
6.2 Establishing a safe connection
Applications that exchange sensitive data with e.g., a backend server
typically need to ensure they have a secure connection to their peer. To
do this, first obtain a non-secure connection to the peer (eg via a TCP
socket connection). Then create an SSL context via
ssl_context/3.
For the client initiating the connection, the role is’client’,
and you should pass options host/1
and cacerts/1
at the very least. If you expect the peer to have a certificate which
would be accepted by your host system, you can pass
cacerts([system(root_certificates)])
, otherwise you will
need a copy of the CA certificate which was used to sign the peer's
certificate. Alternatively, you can pass cert_verify_hook/1
to write your own custom validation for the peer's certificate.
Depending on the requirements, you may also have to provide your /own/
certificate if the peer demands mutual authentication. This is done via
the
certificate_file/1, key_file/1
and either password/1
or
pem_password_hook/1.
Once you have the SSL context and the non-secure stream, you can call ssl_negotiate/5 to obtain a secure stream. ssl_negotiate/5 will raise an exception if there were any certificate errors that could not be resolved.
The peer behaves in a symmetric fashion: First, a non-secure connection is obtained, and a context is created using ssl_context/3 with the role set to server. In the server case, you must provide certificate_file/1 and key_file/1, and then either password/1 or pem_password_hook/1. If you require the other party to present a certificate as well, then peer_cert(true) should be provided. If the peer does not present a certificate, or the certificate cannot be validated as trusted, the connection will be rejected.
By default, revocation is not checked. To enable certificate revocation checking, pass require_crl(true) when creating the SSL context. See section 6 for more information about revocations.