Overview

Certify is a C++11, header only library, which provides an abstraction of platform-specific operations, such as using X.509 certificates from the Operating System’s keystore to perform peer authentication during a TLS handshake. This library is intended for use with Boost.ASIO’s SSL components. Users of this library are assumed to have at least a basic understanding of the implementation of the Networking TS in Boost.ASIO. Beginners are recommended to start with the ASIO tutorial in Boost.Beast documentation to be able to follow examples in this library.

Note: Certify is not yet part of Boost.

Getting started

Certify is header-only, so no build step is required to use it.

Requirements

  • C++11

  • Boost (mostly Boost.ASIO)

  • OpenSSL (OpenSSL 1.0.2 or higher)

Building tests and examples

Before building, you need to clone/copy Certify into the libs/ subdirectory and bootstrap the b2 build tool:

cd boost
cd libs/
git clone https://github.com/djarek/certify.git
cd ..
./b2 libs/certify/test libs/certify/examples

Tests and examples can be built using the following command:

cd boost
./b2 libs/certify/test libs/certify/examples

For more information about additional flags and configuration steps see the Boost Wiki.

Building tests and examples requires OpenSSL installed. If OpenSSL is installed in a non-system location, you will need to copy the user-config.jam file into your home directory and set the OPENSSL_ROOT environment variable to the path that contains an installation of OpenSSL.

  • Ubuntu/Debian

If installed into a system directory, OpenSSL will be automatically found and used.

sudo apt install libssl-dev
  • Windows

Replace path in the following code snippets with the path you installed vcpkg to. Examples assume a 32-bit build, if you build a 64-bit version replace x32-windows with x64-windows in the path.

Using vcpkg and CMD:

vcpkg install openssl --triplet x32-windows
SET OPENSSL_ROOT=path\installed\x32-windows

Using vcpkg and PowerShell:

vcpkg install openssl --triplet x32-windows
$env:OPENSSL_ROOT = "path\x32-windows"

Using vcpkg and bash:

vcpkg.exe install openssl --triplet x32-windows
export OPENSSL_ROOT=path/x32-windows
  • MacOS

Using brew:

brew install openssl
export OPENSSL_ROOT=$(brew --prefix openssl)

TLS primer

TLS (Transport Layer Security, specified in RFC8447) is a network protocol which provides confidentiality and authentication between peers of a network connection. TLS sessions are represented by instances of the boost::asio::ssl::stream<NextLayer> class template. TLS connections share a number of configuration variables and state, which are stored in a boost::asio::ssl::context. It is safe to use a boost::asio::ssl::context context for multiple connections that run in parallel on different threads, however accessing the context directly may result in data races or race conditions.

A TLS session lasts between the initial handshake and shutdown. A handshake is a bidirectional exchange of configuration information and credentials, during which both peers agree on protocol configuration such as version or cipher. Both handshake and shutdown are initiated by the client. If peers cannot agree on a common set of configuration values or authentication fails, the entire handshake operation will fail. In typical use cases, the client authenticates the server based on their X.509 certificate, by verifying it with the use of public certificates of trusted organizations, known as certificate authorities. In order to verify a peer’s certificate, the implementation retrieves (one or more) public certificates from its key store. If verification of a certificate chain fails, the chain will be considered to be self-signed and the user is presented with an option to either continue the handshake or discontinue with an error. The verification procedure of a certificate chain presented by an HTTPS server is specified in RFC2818.

Some servers serve multiple hostnames using the same TCP endpoint and usually cannot determine which hostname to serve to a particular client unless additional information is provided. TLS-SNI is an extension that enables a client to indicate to a server which hostname it is trying to connect to. Some servers require this an SNI hostname to be sent in a TLS handshake, because multiple hostnames may be served by the same public-facing IP, without requiring all those sites to use the same certificate.

A client that wants to indicate to the server that a session is to be terminated in a clean way, it performs a shutdown operation. Some servers optimize this step out, when they can determine ahead of time that a peer will no longer send any requests and close the connection without waiting for a shutdown to complete.

Reference

The contents of the library are in namespace boost::certify.

HTTPS verification, <boost/certify/https_verification.hpp>

enable_native_https_server_verification(context)

void
enable_native_https_server_verification(asio::ssl::context& context);

Enables the use of the native certificate validation mechanism during a TLS handshake. Certificate verification is first performed using CA certificates imported into OpenSSL. If that fails because of missing CA certificates, the library will utilize platform specific APIs to try and complete the process.

If verification, using the platform-specific API, fails, the handshake operation will fail with an error indicating that the certificate chain was self-signed. A more detailed error can be retrieved after the handshake operation completes, using SSL_get_verify_result.

The library uses SSL_CTX_set_cert_verify_callback in order to override the default certificate verification procedure. If a regular verification callback is set using asio::ssl::stream::set_verify_callback, it will be invoked during the first phase of certificate verification, in the same way a default-configured asio::ssl::context would.

set_server_hostname(stream, string_view, error_code)

template<class NextLayer>
void
set_server_hostname(asio::ssl::stream<NextLayer>& stream,
                    string_view hostname,
                    system::error_code& ec);

Sets the expected server hostname, which will be checked during the verification process. The hostname must not contain \0 characters. If setting the hostname results in a failure, ec will contain the error code.

set_server_hostname(stream, string_view)

template<class NextLayer>
void
set_server_hostname(asio::ssl::stream<NextLayer>& stream, string_view hostname);

Sets the expected server hostname, which will be checked during the verification process. The hostname must not contain \0 characters. If setting the hostname results in a failure, an instance of system::system_error will be thrown.

TLS extensions, <boost/certify/extensions.hpp>

This file contains extensions to the implementation of asio::ssl.

sni_hostname(stream)

template<class AsyncStream>
boost::string_view
sni_hostname(boost::asio::asio::ssl::stream<AsyncStream> const& stream);

Returns the SNI hostname set on the provided stream or a default constructed string_view if none was set.

sni_hostname(stream, hostname, error_code)

template<class AsyncStream>
void
sni_hostname(boost::asio::asio::ssl::stream<AsyncStream>& stream,
             std::string const& hostname,
             boost::system::error_code& ec);

Sets the provided SNI hostname on the provided stream. On success, ec is cleared, on error it will contain an error.

sni_hostname(stream, hostname)

template<class AsyncStream>
void
sni_hostname(asio::ssl::stream<AsyncStream>& stream,
             std::string const& hostname);

Sets the provided SNI hostname on the provided stream. On error a boost::system::system_error is thrown.