Mbed TLS: TLS guide
This guide describes the implementation of a TLS client in Mbed TLS.
The guide covers basic aspects of initiating a secure TLS connection, including certificate validation and hostname verification. When various alternative approaches are possible, the guide presents each of them and specifies their use cases to help you choose which approach suits your needs best.
- We work with the API in C of Mbed TLS, version 2.16.9.
- We assume the server to communicate with is at
x509errors.org
and accepts TLS connections on a standard port443
.
Note: For now, the guide does not cover revocation checking and advanced techniques that may follow after the connection is already established, e.g. session resumption.
Note: Mbed TLS does not support online revocation checking of any kind. Use another library if that is your requirement.
Preparing necessary data structures
Mbed TLS requires quite a lot of structures to be initialized before we start.
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/entropy.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
/* Wrapper of the socket descriptor.
** This will take care of the underlying TCP/IP connection. */
mbedtls_net_context server_fd;
mbedtls_net_init(&server_fd);
/* Entropy (randomness source) context.
** Necessary to produce random data during the TLS handshake. */
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);
/* Context for random number generation.
** Again, needed to produce random data during the handshake. */
mbedtls_ctr_drbg_context drbg;
mbedtls_ctr_drbg_init(&drbg);
/* TLS context which represents our session. */
mbedtls_ssl_context ssl;
mbedtls_ssl_init(&ssl);
/* Configuration to use within TLS. */
mbedtls_ssl_config conf;
mbedtls_ssl_config_init(&conf);
/* Seed the random number generator. */
if (mbedtls_ctr_drbg_seed(&drbg, mbedtls_entropy_func, &entropy, NULL, 0) != 0) {
exit(EXIT_FAILURE);
}
/* Assign the random number generator to the TLS config. */
if (mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &drbg) != 0) {
exit(EXIT_FAILURE);
}
/* Assign the TLS config to the TLS context. */
if (mbedtls_ssl_setup(&ssl, &conf) != 0) {
exit(EXIT_FAILURE);
}
Relevant links
-
mbedtls_net_init
(Mbed TLS docs) -
mbedtls_entropy_init
(Mbed TLS docs) -
mbedtls_ctr_drbg_init
(Mbed TLS docs) -
mbedtls_ssl_init
(Mbed TLS docs) -
mbedtls_ssl_config_init
(Mbed TLS docs) -
mbedtls_ctr_drbg_seed
(Mbed TLS docs) -
mbedtls_ssl_conf_rng
(Mbed TLS docs) -
mbedtls_ssl_setup
(Mbed TLS docs)
Configuring the session settings
For the connection to be functional and secure, we must set multiple options beforehand.
/* Set defaults for the TLS configuration.
** This is the recommended setting. */
if (mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
exit(EXIT_FAILURE);
}
/* However, we accept only TLS 1.2 and higher. */
mbedtls_ssl_conf_min_version(&conf, MBEDTLS_SSL_MAJOR_VERSION_3,
MBEDTLS_SSL_MINOR_VERSION_3);
/* We need to set the option to validate the peer certificate chain.
** If we skipped this step, an active attacker could impersonate the server. */
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
/* Set hostname for verification.
** Not setting the hostname would mean that we would accept a certificate of any trusted server.
** It also sets the Server Name Indication TLS extension.
** This is required when multiple servers are running at the same IP address (virtual hosting). */
if (mbedtls_ssl_set_hostname(&ssl, "x509errors.org") != 0) {
exit(EXIT_FAILURE);
}
Relevant links
-
mbedtls_ssl_config_defaults
(Mbed TLS docs) -
mbedtls_ssl_conf_min_version
(Mbed TLS docs) -
mbedtls_ssl_conf_authmode
(Mbed TLS docs) -
mbedtls_ssl_set_hostname
(Mbed TLS docs) - Server Name Indication (RFC 6066)
Specifying trusted root authority certificates
Trusted root certs are usually found within a directory such as /etc/ssl/certs
. If they are not concatenated, concatenate them using e.g. the cat
command. We will now assume that a file trusted_certs.pem
contains all trusted root certificates.
When using Mbed TLS, it is necessary to concatenate all trusted CA certificates into one file in the PEM format.
In some cases, it might be useful to trust an arbitrary certificate authority. This could be the case during testing or within company intranets. In that case, use arbitrary trusted CA certificate files instead.
/* Structure to load trusted root certs into. */
mbedtls_x509_crt ca_certs;
mbedtls_x509_crt_init(&ca_certs);
/* Parse the file with root certificates. */
if (mbedtls_x509_crt_parse_file(&ca_certs, "trusted_certs.pem") != 0) {
exit(EXIT_FAILURE);
}
/* Set the certificates as trusted for this session. */
mbedtls_ssl_conf_ca_chain(&conf, &ca_certs, NULL);
Relevant links
-
mbedtls_x509_crt_init
(Mbed TLS docs) -
mbedtls_x509_crt_parse_file
(Mbed TLS docs) -
mbedtls_ssl_conf_ca_chain
(Mbed TLS docs)
Optional: Checking revocation using local CRLs
Mbed TLS natively provides only offline revocation checking. That is, the revocation list must already be present locally. If the CRL is contained in crl.pem
, we include it in the configuration as follows.
In the most recent versions (Mbed TLS 3.7), it may be possible to implement online revocation checks manually. We will include it in the guide when this version becomes more widely adopted.
/* Structure to load the CRL into. */
mbedtls_x509_crl crl;
mbedtls_x509_crl_init(&crl);
/* Load the CRL from file. */
if (mbedtls_x509_crl_parse_file(&crl, "crl.pem") != 0) {
exit(EXIT_FAILURE);
}
/* Assign it to the config, together with the trusted CA file. */
mbedtls_ssl_conf_ca_chain(&conf, &ca_certs, &crl);
Relevant links
-
mbedtls_x509_crl_init
(Mbed TLS docs) -
mbedtls_x509_crl_parse_file
(Mbed TLS docs) -
mbedtls_ssl_conf_ca_chain
(Mbed TLS docs)
Initializing a TLS connection
At this point, we can initialize a TCP/IP connection and then build the TLS connection on top.
/* Initialize the underlying TCP/IP connection */
if (mbedtls_net_connect(&server_fd, , opts.port, MBEDTLS_NET_PROTO_TCP) != 0) {
exit(EXIT_FAILURE);
}
/* Link the socket wrapper to our TLS session structure.
** Also set the onput/ouput function that we will use to transfer application data. */
mbedtls_ssl_set_bio(&ssl, &server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
/* Perform the TLS handshake.
** During this procedure, the peer certificate is validated. */
if (mbedtls_ssl_handshake(&ssl) != 0) {
exit(EXIT_FAILURE);
}
Relevant links
-
mbedtls_net_connect
(Mbed TLS docs) -
mbedtls_ssl_set_bio
(Mbed TLS docs) -
mbedtls_ssl_handshake
(Mbed TLS docs) - TLS handshake details (RFC 5246)
Optional: Checking the result of peer certificate validation
If certificate validation fails, mbedtls_ssl_handshake()
will always fail with the same error message. In that case, it is often useful to examine the specific certificate validation error as follows. You can find explanations of certificate validation messages in the official documentation or on our dedicated page.
/* Manually retrieve the result of certificate validation. */
uint32_t res = mbedtls_ssl_get_verify_result(&ssl);
/* Print the result of certificate validation as a string into the standard error output. */
char message_buffer[2048];
mbedtls_x509_crt_verify_info(message_buffer, 2048, "", res);
fprintf(stderr, "%s", message_buffer);
Relevant links
-
mbedtls_ssl_get_verify_result
(Mbed TLS docs) -
mbedtls_x509_crt_verify_info
(Mbed TLS docs) - Certificate path validation (RFC 5280)
- Certificate validation errors (MbedTLS docs)
- Certificate validation errors (x509errors.org)
Sending and receiving data using the TLS connection
When the connection is successfully established, we can share application data with the server. These two functions provide the basic interface.
/* Prepare a message and send it to the server. */
char *message = "Hello server";
if (mbedtls_ssl_write(ssl, message, strlen(message)) != 1) {
exit(EXIT_FAILURE);
}
/* Prepare a static buffer for the response and read the response into that buffer. */
char buffer[4096];
if (mbedtls_ssl_read(ssl, buffer, 4096) != 1) {
exit(EXIT_FAILURE);
}
Relevant links
-
mbedtls_ssl_write
(Mbed TLS docs) -
mbedtls_ssl_read
(Mbed TLS docs)
Closing the TLS connection
The client is usually the one to indicate that the connection is finished. When we want the connection closed, the following steps are performed.
/* Gracefully close the connection by sending the "close notify" message to the server. */
if (mbedtls_ssl_close_notify(&ssl) != 0) {
exit(EXIT_FAILURE);
}
/* Clean up all used resources and structures. */
mbedtls_ssl_free(&ssl);
mbedtls_x509_crt_free(&ca_certs);
mbedtls_ssl_config_free(&conf);
mbedtls_net_free(&server_fd);
mbedtls_ctr_drbg_free(&drbg);
mbedtls_entropy_free(&entropy);
Relevant links
-
mbedtls_ssl_close_notify
(Mbed TLS docs) -
mbedtls_ssl_free
(Mbed TLS docs) -
mbedtls_x509_crt_free
(Mbed TLS docs) -
mbedtls_ssl_config_free
(Mbed TLS docs) -
mbedtls_net_free
(Mbed TLS docs) -
mbedtls_ctr_drbg_free
(Mbed TLS docs) -
mbedtls_entropy_free
(Mbed TLS docs)