SSL/TLS

TLS for servers

In this section, we give a very short and quick description on how to enable SSL/TLS for a server (listening) connection - for example, HTTPS server.

  • Obtain SSL certificate file and private key file (or create your own self-signed certificate, see below)
  • In the event handler function, call mg_tls_init() on the MG_EV_ACCEPT event:
    static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
      if (ev == MG_EV_ACCEPT) {
        struct mg_tls_opts opts = {
          .cert = "cert.pem",    // Certificate file
          .certkey = "key.pem",  // Private key file
        };
        mg_tls_init(c, &opts);
      }
      ...
    }
    
  • Rebuild the application, see build instructions below

You can see example code in

TLS for clients

In order to TLS-enable client connections,

  • Obtain SSL CA (Certificate Authority) file

  • In the event handler function, call mg_tls_init() on the MG_EV_CONNECT event:

  • If you need to do something once the TLS handshake is finished, handle the MG_EV_TLS_HS event:

  • Rebuild the application, see build instructions below

You can see example code in

Certificates overview

TLS provides two major benefits:

  • traffic encryption, which makes it impossible to sniff and look inside the traffic, and
  • authentication, which makes it possible to one side of TLS connection verify the identity of the other side.

Here we're talking about the authentication. Authentication is implemented via certificates. A certificate has two parts - public and private. Talking in practical terms, three files are required to implement TLS authentication:

  • TLS certificate. This is a "public" part. For example, TLS-enabled server sends it to the client during TLS handshake
  • TLS private key. This is a "private" part
  • TLS Certificate Authority (CA) file. It is used for verification of the "public" certificate sent by a server

TLS certificates are obtained from services like Let's Encrypt. The other possibility is self-signed certificates, which are mainly used for development.

Self-signed certificates

It is possible to generate certificate files. Note that servers using those files will not be trusted by the browsers, because browsers use a pre-installed CA files and know nothing about your generated certificates.

A command below uses openssl command line tool to generate a self-signed server certificate and a key file:

$ openssl req  -nodes -new -x509  -keyout key.pem -out cert.pem

Two way TLS

Normally, when a client makes a connection to a TLS-enabled server, a server sends its certificate to the client and client verifies it using its own CA (Certificate Authority) file. This way a client authenticates a server.

In the most common situation, a client verifies a server, but a server does not verify a client. For example, browsers (clients) use a big CA file (or many CA files) to verify HTTPS servers.

Clients can also provide certificates during TLS handshake, and a server can verify it using a CA file. When both client and server use certificates, and verify the other side using CA file, is called two-way TLS. In order to implement two-way TLS, both client and server must have cert, certkey and ca specified in the mg_tls_opts:

static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
  if (ev == MG_EV_CONNECT) {
    struct mg_str host = mg_url_host(s_url);
    if (mg_url_is_ssl(s_url)) {
      struct mg_tls_opts opts = {
        .ca = "ca.pem",         // CA file
        .cert = "cert.pem",     // Certificate file
        .certkey = "key.pem",   // Private key file
        .srvname = host,        // Only for client connections
      };
      mg_tls_init(c, &opts);
    }

Two way TLS certificates

It is possible to generate self-signed ca.pem, and cert + key pairs for for both client and server for two-way (mutual) authentication. There how it is done using openssl command line tool:

## Common parameters
$ SUBJ="/C=IE/ST=Dublin/L=Docks/O=MyCompany/CN=howdy"

## Generate CA
$ openssl genrsa -out ca.key 2048
$ openssl req -new -x509 -days 365 -key ca.key -out ca.crt \
  -subj /C=IE/ST=Dublin/L=Docks/O=mos/CN=me 

## Generate client cert
$ openssl genrsa -out client.key 2048
$ openssl req -new -key client.key -out client.csr -subj $SUBJ
$ openssl x509 -req -days 365 -in client.csr -CA ca.crt \
  -CAkey ca.key -set_serial 01 -out client.crt

## Generate server cert
$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr -subj $SUBJ
$ openssl x509 -req -days 365 -in server.csr -CA ca.crt \
  -CAkey ca.key -set_serial 01 -out server.crt

How to build

Use the following compile options:

  • MG_ENABLE_MBEDTLS=1 to build for MbedTLS
  • MG_ENABLE_OPENSSL=1 to build for OpenSSL

Example build on Linux (Ubuntu) using the installed version of mbedTLS:

$ cc main.c mongoose.c -DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509

Example build on Linux (Ubuntu) using the installed version of OpenSSL:

$ cc main.c mongoose.c -DMG_ENABLE_OPENSSL=1 -lssl -lcrypto

The following instructions assume you've already followed the Build Tools tutorial to setup your development environment.

Linux

Assuming Ubuntu Linux, start a terminal and install the library of your choice:

sudo apt -y update
sudo apt -y install libmbedtls-dev
sudo apt -y install libssl-dev

Then use the CFLAGS_EXTRA argument to pass the necessary additional compile options, as follows:

  • make CFLAGS_EXTRA="-DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509" to build for MbedTLS
  • make CFLAGS_EXTRA="-DMG_ENABLE_OPENSSL=1 -lssl -lcrypto" to build for OpenSSL

See also how to build MbedTLS below.

For other similar distributions and operating systems, check pkg-config below

MacOS

Start a terminal, and install the library of your choice:

brew install mbedtls
brew install openssl

Then use the CFLAGS_EXTRA argument to pass the necessary additional compile options, as follows:

  • make CFLAGS_EXTRA="-DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509 -I$(MBEDTLS_DIR)/include -L$(MBEDTLS_DIR)/lib" to build for MbedTLS
  • make CFLAGS_EXTRA="-DMG_ENABLE_OPENSSL=1 -lssl -lcrypto -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib" to build for OpenSSL

See also how to build MbedTLS below

If you see compiler errors messages, see pkg-config below

Windows

See how to build MbedTLS below

Build MbedTLS

The Makefile in the example project will pull a stable and tested version of MbedTLS from its Github repository, and build it on the project directory:

make mbedtls

Then use the CFLAGS_EXTRA argument to pass the necessary additional compile options, as follows:

  • make CFLAGS_EXTRA="-DMG_ENABLE_MBEDTLS=1 -lmbedtls -lmbedcrypto -lmbedx509 -Imbedtls/include -Lmbedtls/library"

pkg-config

If, when using your system libraries, you see a compiler error message like "Cannot find .... file", your system needs extra paths for includes and/or libs, and/or link options; usually the former. This means adding -I path/to/include to tell where installed TLS headers are, and/or -L path/to/lib to tell where installed TLS shared libs are.

These paths and link options are usually determined in GNU/Linux-like systems using pkg-config. Sometimes the package uses the standard paths and no extra paths are needed; others they are installed in a different path to avoid conflicts and flags are required:

$ pkg-config --cflags --libs openssl
-lssl -lcrypto
$ pkg-config --cflags --libs openssl11
-I/usr/include/openssl11  -L/usr/lib64/openssl11 -lssl -lcrypto
$ pkg-config --cflags --libs-only-L openssl11
-I/usr/include/openssl11  -L/usr/lib64/openssl11
$ pkg-config --libs-only-l openssl11
-lssl -lcrypto

This requires that the relevant packages have installed their packagename.pc files in the default path where pkg-config will search for them; otherwise you have to set the PKG_CONFIG_PATH environment variable to where they reside.

To pass these extra options to the make command, extend the CFLAGS_EXTRA argument as, for example, we did for MacOS above.

The link options are mostly the same among systems and usually there is no need to add to them.

Check your system for relevant paths and compiler flags