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 theMG_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 theMG_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
- The MQTT client example
- The HTTP client example
- The TCP client and server example
- The SMTP client example
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 MbedTLSMG_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 MbedTLSmake 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 MbedTLSmake 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