Raw TCP/IP networking is dangerous for two reasons:
Transport Layer Security (TLS) and its predecessor Secure Socket Layer (SSL), which are both often collectively called SSL, solve both problems. SSL uses:
SSL allows agents to talk in private and create secure web services.
The SWI-Prolog library(ssl) library provides an API to 
turn a pair of arbitrary Prolog wire streams into SSL powered 
encrypted streams. Note that secure protocols such as secure HTTP simply 
run the plain protocol over (SSL) encrypted streams.
The library(crypto) library provides additional 
predicates related to cryptography and authentication, secure hashes and 
elliptic curves.
Cryptography is a difficult topic. If you just want to download documents from an HTTPS server without worrying much about security, http_open/3 will do the job for you. As soon as you have higher security demands we strongly recommend you to read enough background material to understand what you are doing. See section 5 for some remarks regarding this implementation. This The Linux Documentation Project page provides some additional background and tips for managing certificates and keys.
An SSL server and client can be built with the (abstracted) predicate 
calls from the table below. The tcp_ predicates are 
provided by library(socket). The predicate ssl_context/3 
defines properties of the SSL connection, while ssl_negotiate/5 
establishes the SSL connection based on the wire streams created by the 
TCP predicates and the context.
The SSL Server The SSL Client ssl_context/3 ssl_context/3 tcp_socket/1 tcp_accept/3 tcp_connect/3 tcp_open_socket/3 stream_pair/3 ssl_negotiate/5 ssl_negotiate/5 
The library is abstracted to communication over streams, and is not 
reliant on those streams being directly attached to sockets. The tcp_ 
calls here are simply the most common way to use the library. Other 
two-way communication channels such as (named), pipes can just as easily 
be used.
client. 
Otherwise, certificate verification may fail when negotiating a secure 
connection.
key_file(+FileName) option. A server must have 
at least one certificate before clients can connect. A client
must have a certificate only if the server demands the client to 
identify itself with a client certificate using the
peer_cert(true) option. If a certificate is provided, it is 
necessary to also provide a matching private key via the
key_file/1 option. To configure multiple 
certificates, use the option certificate_key_pairs/1 
instead. Alternatively, use
ssl_add_certificate_key/4 
to add certificates and keys to an existing context.
password(+Text) 
or
pem_password_hook(:Goal) option.
call(Goal, +SSL, -Password) 
and typically unifies
Password with a string containing the password.
require_crl(true) 
and provide neither of these options, verification will necessarily fail
require_crl(true) if you want 
CRLs to actually be checked by OpenSSL.
system(root_certificates) 
uses a list of trusted root certificates as provided by the OS. See
system_root_certificates/1 
for details.
Additional verification of the peer certificate as well as accepting certificates that are not trusted by the given set can be realised using the hook cert_verify_hook(:Goal).
call(Goal, +SSL,
     +ProblemCertificate, +AllCertificates, +FirstCertificate,
     +Error)
In case the certificate was verified by one of the provided 
certifications from the cacert_file option, Error is 
unified with the atom verified. Otherwise it contains the 
error string passed from OpenSSL. Access will be granted iff the 
predicate succeeds. See load_certificate/2 
for a description of the certificate terms. See cert_accept_any/5 
for a dummy implementation that accepts any certificate.
prime256v1 
is used by default.
true, close the raw streams if the SSL 
streams are closed. Default is false.
true (default is false), the server sends 
TLS
close_notify when closing the connection. In addition, this 
mitigates truncation attacks for both client and server role: If 
EOF is encountered without having received a TLS shutdown, an exception 
is raised. Well-designed protocols are self-terminating, and this attack 
is therefore very rarely a concern.
sslv3, tlsv1, tlsv1_1, tlsv1_2 
and
tlsv1_3. This option is available with OpenSSL 1.1.0 and 
later, and should be used instead of disable_ssl_methods/1.
sslv3, tlsv1, tlsv1_1, tlsv1_2 
and
tlsv1_3. This option is available with OpenSSL 1.1.0 and 
later, and should be used instead of disable_ssl_methods/1.
sslv2, sslv3, sslv23,
tlsv1, tlsv1_1 and tlsv1_2. This 
option is deprecated starting with OpenSSL 1.1.0. Use min_protocol_version/1 
and
max_protocol_version/1 instead.
disable_ssl_methods above. 
Using this option is discouraged. When using OpenSSL 1.1.0 or later, 
this option is ignored, and a version-flexible method is used to 
negotiate the connection. Using version-specific methods is deprecated 
in recent OpenSSL versions, and this option will become obsolete and 
ignored in the future.
call(Goal, +SSL0, +HostName, -SSL)
Given the current context SSL0, and the host name of the client request, the predicate computes SSL which is used as the context for negotiating the connection. The first solution is used. If the predicate fails, the default options are used, which are those of the encompassing ssl_context/3 call. In that case, if no default certificate and key are specified, the client connection is rejected.
===
call(Goal, +SSLCtx0, +ListOfClientProtocols, -SSLCtx1, -SelectedProtocol) 
===
If this option is unset and the alpn_protocols/1 option 
is set, then the first common protocol between client & server will 
be selected.
| Role | is one of serverorclientand denotes whether the
SSL instance will have a server or client role in the 
established connection. | 
| SSL | is a SWI-Prolog blob of type ssl_context, 
i.e., the type-test for an SSL context isblob(SSL, ssl_context). | 
This predicate allows dual-stack RSA and ECDSA servers (for example), 
and is an alternative for using the
certificate_key_pairs/1 option. As of OpenSSL 1.0.2, 
multiple certificate types with completely independent certificate 
chains are supported. If a certificate of the same type is added 
repeatedly to a context, the result is undefined. Currently, up to 12 
additional certificates of different types are admissible.
After a successful handshake and finishing the communication the user 
must close SSLRead and SSLWrite, for example using
call_cleanup(close(SSLWrite), close(SSLRead)). If the SSL
context (created with ssl_context/3 
has the option
close_parent(true) (default false), closing SSLRead 
and
SSLWrite also closes the original PlainRead and PlainWrite 
streams. Otherwise these must be closed explicitly by the user.
  http_open(HTTPS_url, In, []),
  ssl_peer_certificate(In, Cert),
  memberchk(subject(Subject), Cert),
  memberchk('CN' = CommonName), Subject)
Note that the OpenSSL CA.pl utility creates certificates 
that have a human readable textual representation in front of the PEM 
representation. You can use the following to skip to the certificate if 
you know it is a PEM certificate:
skip_to_pem_cert(In) :-
      repeat,
      (   peek_char(In, '-')
      ->  !
      ;   skip(In, 0'\n),
          at_end_of_stream(In), !
      ).
revoked(+Serial, DateOfRevocation)system(root_certificates). The list is obtained using an OS 
specific process. The current implementation is as follows:
"ROOT" 
certificates from the OS.
system_cacert_filename. The initial value of this flag 
is operating system dependent. For security reasons, the flag can only 
be set prior to using the SSL library. For example:
:- use_module(library(ssl)).
:- set_prolog_flag(system_cacert_filename,
                   '/home/jan/ssl/ca-bundle.crt').
private_key(KeyTerm) 
where KeyTerm is an rsa/8 term 
representing an RSA key, or ec/3 for EC 
keys.public_key(KeyTerm) where KeyTerm is an rsa/8 
term representing an RSA key, or ec/3 for 
EC keys.
  http_open('https:/...', In,
            [ cert_verify_hook(cert_accept_any)
            ])
Secure ciphers must guarantee forward secrecy, and must mitigate all known critical attacks. As of 2018, using these ciphers allows you to obtain grade A on https://www.ssllabs.com. For A+, you must also enable HTTP Strict Transport Security (HSTS) by sending a suitable header field in replies.
Note that obsolete ciphers must be disabled to reliably prevent protocol downgrade attacks.
The Ciphers list is read from the setting ssl:secure_ciphers 
and can be controlled using set_setting/2 
and other predicates from
library(settings).
BEWARE: This list must be changed when attacks on these ciphers become known! Keep an eye on this setting and adapt it as necessary in the future.
This library provides bindings to functionality of OpenSSL that is related to cryptography and authentication, not necessarily involving connections, sockets or streams.
A basic design principle of this library is that its default algorithms are cryptographically secure at the time of this writing. We will change the default algorithms if an attack on them becomes known, and replace them by new defaults that are deemed appropriate at that time.
This may mean, for example, that where sha256 is 
currently the default algorithm, blake2s256 or some other 
algorithm may become the default in the future.
To preserve interoperability and compatibility and at the same time allow us to transparently update default algorithms of this library, the following conventions are used:
This allows application programmers to inspect which algorithm was actually used, and store it for later reference.
For example:
?- crypto_data_hash(test, Hash, [algorithm(A)]). Hash = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08', A = sha256.
This shows that at the time of this writing, sha256 was 
deemed sufficiently secure, and was used as default algorithm for 
hashing.
You therefore must not rely on which concrete algorithm is being used by default. However, you can rely on the fact that the default algorithms are secure. In other words, if they are not secure, then this is a mistake in this library, and we ask you to please report such a situation as an urgent security issue.
In the context of this library, bytes can be represented as lists of integers between 0 and 255. Such lists can be converted to and from hexadecimal notation with the following bidirectional relation:
Example:
?- hex_bytes('501ACE', Bs).
Bs = [80, 26, 206].
Almost all cryptographic applications require the availability of numbers that are sufficiently unpredictable. Examples are the creation of keys, nonces and salts. With this library, you can generate cryptographically strong pseudo-random numbers for such use cases:
One way to relate such a list of bytes to an integer is to use CLP(FD) constraints as follows:
:- use_module(library(clpfd)).
bytes_integer(Bs, N) :-
        foldl(pow, Bs, 0-0, N-_).
pow(B, N0-I0, N-I) :-
        B in 0..255,
        N #= N0 + B*256^I0,
        I #= I0 + 1.
With this definition, you can generate a random 256-bit integer from a list of 32 random bytes:
?- crypto_n_random_bytes(32, Bs), bytes_integer(Bs, I). Bs = [98, 9, 35, 100, 126, 174, 48, 176, 246|...], I = 109798276762338328820827...(53 digits omitted).
The above relation also works in the other direction, letting you translate an integer to a list of bytes. In addition, you can use hex_bytes/2 to convert bytes to tokens that can be easily exchanged in your applications. This also works if you have compiled SWI-Prolog without support for large integers.
A hash, also called digest, is a way to verify the integrity of data. In typical cases, a hash is significantly shorter than the data itself, and already miniscule changes in the data lead to different hashes.
The hash functionality of this library subsumes and extends that of
library(sha), library(hash_stream) and library(md5) 
by providing a unified interface to all available digest algorithms.
The underlying OpenSSL library (libcrypto) is 
dynamically loaded if
either library(crypto) or library(ssl) 
are loaded. Therefore, if your application uses library(ssl), 
you can use library(crypto) for hashing without increasing 
the memory footprint of your application. In other cases, the 
specialised hashing libraries are more lightweight but less general 
alternatives to library(crypto).
The most important predicates to compute hashes are:
md5 (insecure), sha1 (insecure), ripemd160,
sha224, sha256, sha384, sha512, sha3_224, sha3_256,
sha3_384, sha3_512, blake2s256 or blake2b512. 
The BLAKE digest algorithms require OpenSSL 1.1.0 or greater, and the 
SHA-3 algorithms require OpenSSL 1.1.1 or greater. The default is a 
cryptographically secure algorithm. If you specify a variable, then that 
variable is unified with the algorithm that was used.
utf8. The other 
meaningful value is octet, claiming that Data 
contains raw bytes.
| Data | is either an atom, string or code-list | 
| Hash | is an atom that represents the hash in hexadecimal encoding. | 
For the important case of deriving hashes from passwords, the following specialised predicates are provided:
crypto_password_hash(Password, Hash, []) and computes a 
password-based hash using the default options.Another important distinction is that equal passwords must yield, with very high probability, different hashes. For this reason, cryptographically strong random numbers are automatically added to the password before a hash is derived.
Hash is unified with an atom that contains the computed hash and all parameters that were used, except for the password. Instead of storing passwords, store these hashes. Later, you can verify the validity of a password with crypto_password_hash/2, comparing the then entered password to the stored hash. If you need to export this atom, you should treat it as opaque ASCII data with up to 255 bytes of length. The maximal length may increase in the future.
Admissible options are:
pbkdf2-sha512, 
which is therefore also the default.
^C. 
Currently, the default is 17, and thus more than one hundred thousand 
iterations. You should set this option as high as your server and users 
can tolerate. The default is subject to change and will likely increase 
in the future or adapt to new algorithms.
Currently, PBKDF2 with SHA-512 is used as the hash derivation function, using 128 bits of salt. All default parameters, including the algorithm, are subject to change, and other algorithms will also become available in the future. Since computed hashes store all parameters that were used during their derivation, such changes will not affect the operation of existing deployments. Note though that new hashes will then be computed with the new default parameters.
The following predicate implements the Hashed Message Authentication Code (HMAC)-based key derivation function, abbreviated as HKDF. It supports a wide range of applications and requirements by concentrating possibly dispersed entropy of the input keying material and then expanding it to the desired length. The number and lengths of the output keys depend on the specific cryptographic algorithms for which the keys are needed.
Admissible options are:
utf8 (default) or octet, denoting the 
representation of Data as in crypto_data_hash/3.
The info/1 option can be used to generate multiple keys 
from a single master key, using for example values such as key 
and
iv, or the name of a file that is to be encrypted.
This predicate requires OpenSSL 1.1.0 or greater.
The following predicates are provided for building hashes incrementally. This works by first creating a context with crypto_context_new/2, then using this context with crypto_data_context/3 to incrementally obtain further contexts, and finally extract the resulting hash with crypto_context_hash/2.
| Context | is an opaque pure Prolog term that is subject to garbage collection. | 
This predicate allows a hash to be computed in chunks, which may be important while working with Metalink (RFC 5854), BitTorrent or similar technologies, or simply with big files.
The following hashing predicates work over streams:
true (default), closing the filter stream also closes 
the original (parent) stream.
A digital signature is a relation between a key and data that only someone who knows the key can compute.
Signing uses a private key, and verifying a signature uses the corresponding public key of the signing entity. This library supports both RSA and ECDSA signatures. You can use load_private_key/3 and load_public_key/2 to load keys from files and streams.
In typical cases, we use this mechanism to sign the hash of data. See hashing (section 3.5). For this reason, the following predicates work on the hexadecimal representation of hashes that is also used by crypto_data_hash/3 and related predicates.
Signatures are also represented in hexadecimal notation, and you can use hex_bytes/2 to convert them to and from lists of bytes (integers).
hex) assumes that Data is an atom, 
string, character list or code list representing the data in hexadecimal 
notation. See rsa_sign/4 for an 
example.
Options:
hex. 
Alternatives are octet, utf8 and text.
Options:
hex. 
Alternatives are octet, utf8 and text.
sha1, sha224, sha256, sha384 
or sha512. The default is a cryptographically secure 
algorithm. If you specify a variable, then it is unified with the 
algorithm that was used.
hex. 
Alternatives are octet, utf8 and text.
This predicate can be used to compute a sha256WithRSAEncryption 
signature as follows:
sha256_with_rsa(PemKeyFile, Password, Data, Signature) :-
    Algorithm = sha256,
    read_key(PemKeyFile, Password, Key),
    crypto_data_hash(Data, Hash, [algorithm(Algorithm),
                                  encoding(octet)]),
    rsa_sign(Key, Hash, Signature, [type(Algorithm)]).
read_key(File, Password, Key) :-
    setup_call_cleanup(
        open(File, read, In, [type(binary)]),
        load_private_key(In, Password, Key),
        close(In)).
Note that a hash that is computed by crypto_data_hash/3 can be directly used in rsa_sign/4 as well as ecdsa_sign/4.
Options:
sha1,
sha224, sha256, sha384 or sha512. 
The default is the same as for rsa_sign/4. 
This option must match the algorithm that was used for signing. When 
operating with different parties, the used algorithm must be 
communicated over an authenticated channel.
hex. 
Alternatives are octet, utf8 and text.
The following predicates provide asymmetric RSA encryption and decryption. This means that the key that is used for encryption is different from the one used to decrypt the data:
Options:
utf8. 
Alternatives are utf8 and octet.
pkcs1. Alternatives are pkcs1_oaep, sslv23 
and none. Note that none should only be used 
if you implement cryptographically sound padding modes in your 
application code as encrypting unpadded data with RSA is insecure
The following predicates provide symmetric encryption and decryption. This means that the same key is used in both cases.
PlainText must be a string, atom or list of codes or characters, and CipherText is created as a string. Key and IV are typically lists of bytes, though atoms and strings are also permitted. Algorithm must be an algorithm which your copy of OpenSSL knows about.
Keys and IVs can be chosen at random (using for example crypto_n_random_bytes/2) or derived from input keying material (IKM) using for example crypto_data_hkdf/4. This input is often a shared secret, such as a negotiated point on an elliptic curve, or the hash that was computed from a password via crypto_password_hash/3 with a freshly generated and specified salt.
Reusing the same combination of Key and IV 
typically leaks at least
some information about the plaintext. For example, identical 
plaintexts will then correspond to identical ciphertexts. For some 
algorithms, reusing an IV with the same Key has 
disastrous results and can cause the loss of all properties that are 
otherwise guaranteed. Especially in such cases, an IV is also 
called a
nonce (number used once). If an IV is not needed for 
your algorithm (such as 'aes-128-ecb') then any value can 
be provided as it will be ignored by the underlying implementation. Note 
that such algorithms do not provide semantic security and are 
thus insecure. You should use stronger algorithms instead.
It is safe to store and transfer the used initialization vector (or nonce) in plain text, but the key must be kept secret.
Commonly used algorithms include:
'chacha20-poly1305''aes-128-gcm''aes-128-cbc'Options:
utf8. 
Alternatives are utf8 and octet.
block. You can disable padding by supplying none 
here. If padding is disabled for block ciphers, then the length of the 
ciphertext must be a multiple of the block size.
For example, with OpenSSL 1.1.0 and greater, we can use the ChaCha20 stream cipher with the Poly1305 authenticator. This cipher uses a 256-bit key and a 96-bit nonce, i.e., 32 and 12 bytes, respectively:
?- Algorithm = 'chacha20-poly1305',
   crypto_n_random_bytes(32, Key),
   crypto_n_random_bytes(12, IV),
   crypto_data_encrypt("this is some input", Algorithm,
               Key, IV, CipherText, [tag(Tag)]),
   crypto_data_decrypt(CipherText, Algorithm,
               Key, IV, RecoveredText, [tag(Tag)]).
Algorithm = 'chacha20-poly1305',
Key = [65, 147, 140, 197, 27, 60, 198, 50, 218|...],
IV = [253, 232, 174, 84, 168, 208, 218, 168, 228|...],
CipherText = <binary string>,
Tag = [248, 220, 46, 62, 255, 9, 178, 130, 250|...],
RecoveredText = "this is some input".
In this example, we use crypto_n_random_bytes/2 to generate a key and nonce from cryptographically secure random numbers. For repeated applications, you must ensure that a nonce is only used once together with the same key. Note that for authenticated encryption schemes, the tag that was computed during encryption is necessary for decryption. It is safe to store and transfer the tag in plain text.
utf8. 
Alternatives are utf8 and octet.
block. You can disable padding by supplying none 
here.
This library provides operations from number theory that frequently arise in cryptographic applications, complementing the existing built-ins and GMP bindings:
true (default is false), 
then a safe prime is generated. This means that P is 
of the form 2*Q + 1 where Q is also prime.
^(-80).
This library provides functionality for reasoning over elliptic curves. Elliptic curves are represented as opaque objects. You acquire a handle for an elliptic curve via crypto_name_curve/2.
A point on a curve is represented by the Prolog term point(X, Y), 
where X and Y are integers that represent the 
point's affine coordinates.
The following predicates are provided for reasoning over elliptic curves:
prime256v1 and
secp256k1.
If you have OpenSSL installed, you can get a list of supported curves via:
$ openssl ecparam -list_curves
As one example that involves most predicates of this library, we explain a way to establish a shared secret over an insecure channel. We shall use elliptic curves for this purpose.
Suppose Alice wants to establish an encrypted connection with Bob. To achieve this even over a channel that may be subject to eavesdrooping and man-in-the-middle attacks, Bob performs the following steps:
This mechanism hinges on a way for Alice to establish the authenticity of the signed message (using predicates like rsa_verify/4 and ecdsa_verify/4), for example by means of a public key that was previously exchanged or is signed by a trusted party in such a way that Alice can be sufficiently certain that it belongs to Bob. However, none of these steps require any encryption!
Alice in turn performs the following steps:
Bob receives j*G in plain text and can arrive at the same shared secret by performing the calculation k*(j*G), which is - by associativity and commutativity of scalar multiplication - identical to the point j*(k*G), which is again Q from which the shared secret can be derived, and the message can be decrypted with crypto_data_decrypt/6.
This method is known as Diffie-Hellman-Merkle key exchange over elliptic curves, abbreviated as ECDH. It provides forward secrecy (FS): Even if the private key that was used to establish the authenticity of Bob is later compromised, the encrypted messages cannot be decrypted with it.
A major attraction of using elliptic curves for this purpose is found in the comparatively small key size that suffices to make any attacks unrealistic as far as we currently know. In particular, given any point on the curve, we currently have no efficient way to determine by which scalar the generator was multiplied to obtain that point. The method described above relies on the hardness of this so-called elliptic curve discrete logarithm problem (ECDLP). On the other hand, some of the named curves have been suspected to be chosen in such a way that they could be prone to attacks that are not publicly known.
As an alternative to ECDH, you can use the original DH key exchange scheme, where the prime field GF(p) is used instead of an elliptic curve, and exponentiation of a suitable generator is used instead of scalar multiplication. You can use crypto_generate_prime/3 to generate a sufficiently large prime for this purpose.
The SSL package provides several libraries dealing with cryptographic 
operations of XML documents. These libraries depend on the sgml 
package. These libraries are part of this package because the
sgml package has no external dependencies and will thus be 
available in any SWI-Prolog installation while configuring and building 
this ssl package is much more involved.
This library uses SAML to exchange messages with an Identity Provider to establish assertions about the current user's session. It operates only as the service end, not the identity provider end.
This library is a partial implementation of the XML encryption standard. It implements the decryption part, which is needed by SAML clients.
| KeyCallback | may be called as follows: 
 
 | 
This library deals with XMLDSIG, RSA signed XML documents.
The SignedDOM must be emitted using xml_write/3 
or
xml_write_canonical/3. If xml_write/3 
is used, the option
layout(false) is needed to avoid changing the layout of the
SignedInfo element and the signed DOM, which 
will cause the signature to be invalid.
ds:Signature element contains a valid 
signature. Certificate is bound to the certificate that 
appears in the element if the signature is valid. It is up to the caller 
to determine if the certificate is trusted or not.
Note: The DOM and SignatureDOM must have 
been obtained using the load_structure/3 
option keep_prefix(true) otherwise it is impossible to 
generate an identical document for checking the signature. See also xml_write_canonical/3.
Using SSL (in this particular case based on the OpenSSL 
implementation) to connect to SSL services (e.g., an https:// 
address) easily gives a false sense of security. This section explains 
some of the pitfalls.1We do not 
claim to be complete, just to start warning you if security is important 
to you. Please make sure you understand (Open)SSL before relying on it.. 
As stated in the introduction, SSL aims at solving two issues: tapping 
information from the wire by means of encryption and make sure that you 
are talking to the right address.
Encryption is generally well arranged as long as you ensure that the 
underlying SSL library has all known security patches installed and you 
use an encryption that is not known to be weak. The Windows version and 
MacOS binaries of SWI-Prolog ships with its own binary of the OpenSSL 
library. Ensure this is up-to-date. On systems that ship with the 
OpenSSL library SWI-Prolog uses the system version. This applies notably 
for all Linux packages. Check the origin and version of the OpenSSL 
libraries and verify there are no more recent security patches regularly 
if security is important to you. The OpenSSL library version as reported 
by SSLeay_version() is available in the Prolog flag
ssl_library_version as illustrated below on Ubuntu 14.04.
?- [library(ssl)]. ?- current_prolog_flag(ssl_library_version, X). X = 'OpenSSL 1.0.1f 6 Jan 2014'.
Whether you are talking to the right address is a complicated issue. The core of the validation is that the server provides a certificate that identifies the server. This certificate is digitally signed by another certificate, and ultimately by a root certificate. (There may be additional links in this chain as well, or there may just be one certificate signed by itself) Verifying the peer implies:
The default https client plugin (library(http/http_ssl_plugin)) 
registers the system trusted root certificate with OpenSSL. This is 
achieved using the option
cacert_file(system(root_certificates)) of ssl_context/3. 
The verification is left to OpenSSL. To the best of our knowledge, the 
current (1.0) version of OpenSSL only implements step (1) of the 
verification process outlined above. This implies that an attacker that 
can control DNS mapping (host name to IP) or routing (IP to physical 
machine) can fake to be a secure host as long as they manage to obtain a 
certificate that is signed from a recognised authority. Version 1.0.2 
supports hostname checking, and will not validate a certificate chain if 
the leaf certificate does not match the hostname. 'Match' here is not a 
simple string comparison; certificates are allowed (subject to many 
rules) to have wildcards in their SubjectAltName field. Care must also 
be taken to ensure that the name we are checking against does not 
contain embedded NULLs. If SWI-Prolog is compiled against a version of 
OpenSSL that does NOT have hostname checking (ie 1.0.0 or earlier), it 
will attempt to do the validation itself. This is not guaranteed to be 
perfect, and it only supports a small subset of allowed wildcards. If 
security is important, use OpenSSL 1.0.2 or higher.
After validation, the predicate ssl_peer_certificate/2 can be used to obtain the peer certificate and inspect its properties.
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.
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!
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 cacert_file/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
cacert_file(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.
Examples of a simple server and client (server.pl and
client.pl as well as a simple HTTPS server (https.pl) 
can be found in the example directory which is located in
doc/packages/examples/ssl relative to the SWI-Prolog 
installation directory. The etc directory contains example 
certificate files as well as a README on the creation of 
certificates using OpenSSL tools.
Accessing an https:// server can be achieved using the 
code skeleton below. The line :- use_module(library(http/http_ssl_plugin)). 
can actually be omitted because the plugin is dynamically loaded by
http_open/3 
if the https scheme is detected. See section 
5 for more information about security aspects.
:- use_module(library(http/http_open)).
:- use_module(library(http/http_ssl_plugin)).
    ...,
    http_open(HTTPS_url, In, []),
    ...
The SWI-Prolog infrastructure provides two main ways to launch an HTTPS server:
library(http/thread_httpd), the server is started 
in HTTPS mode by adding an option ssl/1 
to
http_server/2. 
The argument of ssl/1 
is an option list that is passed as the third argument to ssl_context/3.
library(http/http_unix_daemon), an HTTPS server 
is started by using the command line argument --https.
Two items are typically specified as, respectively, options or additional command line arguments:
password option, the pem_password_hook 
callback or, in case of the Unix daemon, via the --pwfile 
or
--password command line options.
Here is an example that uses the self-signed demo certificates distributed with the SSL package. As is typical for publicly accessible HTTPS servers, this version does not require a certificate from the client:
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_ssl_plugin)).
https_server(Port, Options) :-
        http_server(reply,
                    [ port(Port),
                      ssl([ certificate_file('etc/server/server-cert.pem'),
                            key_file('etc/server/server-key.pem'),
                            password("apenoot1")
                          ])
                    | Options
                    ]).
There are two hooks that let you extend HTTPS servers with custom definitions:
http:ssl_server_create_hook(+SSL0, -SSL, +Options): 
This extensible predicate is called exactly once, after creating 
an HTTPS server with Options. If this predicate succeeds,
SSL is the context that is used for negotiating all 
new connections. Otherwise, SSL0 is used, which is the 
context that was created with the given options.
http:ssl_server_open_client_hook(+SSL0, -SSL, +Options): 
This predicate is called before each connection that the server 
negotiates with a client. If this predicate succeeds, SSL 
is the context that is used for the new connection. Otherwise, SSL0 
is used, which is the context that was created when launching the server.
Important use cases of these hooks are running dual-stack RSA/ECDSA servers, updating certificates while the server keeps running, and tweaking SSL parameters for connections. Use ssl_set_options/3 to create and configure copies of existing contexts in these hooks.
The example file https.pl also provides a server that
does require the client to show its certificate. This provides an 
additional level of security, often used to allow a selected set of 
clients to perform sensitive tasks.
Note that a single Prolog program can call http_server/2 
with different parameters to provide services at several security levels 
as described below. These servers can either use their own dispatching 
or commonly use http_dispatch/1 
and check the port property of the request to verify they 
are called with the desired security level. If a service is approached 
at a too low level of security, the handler can deny access or use HTTP 
redirect to send the client to to appropriate interface.
The above expects Prolog to be accessible directly from the internet. This is becoming more popular now that services are often deployed using virtualization. If the Prolog services are placed behind a reverse proxy, HTTPS implementation is the task of the proxy server (e.g., Apache or Nginx). The communication from the proxy server to the Prolog server can use either plain HTTP or HTTPS. As plain HTTP is easier to setup and faster, this is typically preferred if the network between the proxy server and Prolog server can be trusted.
Note that the proxy server must decrypt the HTTPS traffic because it must decide on the destination based on the encrypted HTTP header. Port forwarding provides another option to make a server running on a machine that is not directly connected to the internet visible. It is not needed to decrypt the traffic using port forwarding, but it is also not possible to realise virtual hosts or path-based proxy rules.
Virtual hosts for HTTPS are available via Server Name Indication (SNI). This is a TLS extension that allows servers to host different domains from the same IP address. See the sni_hook/1 option of ssl_context/3 for more information.
The development of the SWI-Prolog SSL interface has been sponsored by Scientific Software and Systems Limited. The current version contains contributions from many people. Besides the mentioned authors, Markus Triska has submitted several patches, and improved and documented the integration of this package with the HTTP infrastructure.