Certificates

Cripts provides a set of convenient classes for introspection into the various TLS certificates that are used. These include both the server certificates used to establish a TLS connections, as well as any client certificates used for mutual TLS.

In the current implementation, these objects only work on X509 certificates as associated with the client and server connections. Let's start off with a simple example of how to use these objects:

do_send_response()
{
  if (client.connection.IsTLS()) {
    const auto tls = cripts::Certs::Server(client.connection);

    client.response["X-Subject"] = tls.subject;
    client.response["X-NotBefore"] = tls.notBefore;
    client.response["X-NotAfter"] = tls.notAfter;
  }
}

Objects

There are two types of objects for the certificates:

Object

Description

cripts::Certs::Server

The certificate used on the connection for TLS handshakes.

cripts::Certs::Client

The mutual TLS (mTLS) certificate used on the connection.

This combined with the two kinds of connections, cripts::Client::Connection and cripts::Server::Connection yields a total of four possible certificate objects. For example, to access the client mTLS provided certificate on a client connection, you would use:

const auto tls = cripts::Certs::Client(cripts::Client::Connection::Get());

Or if you are using the convenience wrappers:

const auto tls = cripts::Certs::Client(client.connection);

X509 Values

As part of the certificate objects, there are a number of values that can be accessed. These values are all based on the X509 standard and can be used to introspect the certificate. The following values are available:

Value

Description

certificate

The raw X509 certificate in PEM format.

signature

The raw signature of the certificate.

subject

The subject of the certificate.

issuer

The issuer of the certificate.

serialNumber

The serial number of the certificate.

notBefore

The date and time when the certificate is valid from.

notAfter

The date and time when the certificate is valid until.

version

The version of the certificate.

SAN Values

We've made special provisions to access the Subject Alternative Name (SAN) values of the certificate. These values are often used to identify the hostnames or IP addresses that the certificate is valid for. Once you have the certificate object, you can access the SAN values as follows:

Field

X509 field

Description

.san

na

An array of tuples with type and string_view of all SANs.

.san.email

GEN_EMAIL

An array of string_view of email addresses.

.san.dns

GEN_DNS

An array of string_view of DNS names.

.san.uri

GEN_URI

An array of string_view of URIs.

.san.ipadd

GEN_IPADD

An array of string_view of IP addresses.

注釈

These arrays are empty if no SAN values are present in the certificate. We also populate these arrays lazily, but they are kept for the lifetime of the certificate object. This means that you can access these values multiple times without incurring additional overhead. Remember that you can use the cripts::Net::IP class to convert the IP addresses into proper IP address objects if needed.

Odds are that you will want to use one of the specific array values, such as .san.uri, which is easily done in a simple loop:

do_remap()
{
  if (client.connection.IsTLS()) {
    const auto tls = cripts::Certs::Server(client.connection);

    for (auto uri : tls.san.uri) {
      // Check the URI string_view
    }
  }
}

You can of course loop over all SAN values, which is where the type of the value would come in handy, and why this is an array of tuples. In this scenario, you would iterate over the tuples like this:

do_remap()
{
  if (client.connection.IsTLS()) {
    const auto tls = cripts::Certs::Server(client.connection);

    for (const [type, san] : tls.san) {
      if (type == cripts::Certs::SAN::URI) {
        // Check the URI string here
      } else if (type == cripts::Certs::SAN::DNS) {
        // Check the DNS string here
      }
    }
  }
}

In addition to traditional C++ iterators, you can also access SAN values by index. Make sure you check the size of the array first, as accessing an out-of-bounds index will give you an empty tuple. Prefer the iterator above, unless you know you want to access a specific element.

Example of an alternative way to loop over all SAN values:

do_remap()
{
  if (client.connection.IsTLS()) {
    const auto tls = cripts::Certs::Server(client.connection);

    size_t san_count = tls.san.size();

    for (size_t i = 0; i < san_count; ++i) {
      const auto [type, san] = tls.san[i];
      // Process the type and san as needed
    }
  }
}