Tuturial: mbedTLS SSL Certificate Verification with Mosquitto, lwip and MQTT

In “Tutorial: Secure TLS Communication with MQTT using mbedTLS on top of lwip” I already used TLS for a secure communication, but I had not enabled server certificate verification. This article is about closing that gap.

MQTT running on NXP FRDM-K64F

Secure MQTT running on NXP FRDM-K64F with lwip and mbed TLS

Outline

In my earlier article I used the following as authentication mode in mbedTLS:

mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);

This is definitely not something I want in production code, as it does not verify the server certificate and would allow a ‘men in the middle’ attack (see “Enable Secure Communication with TLS and the Mosquitto Broker“). This article explains how to enable and use certificate verification with mbed TLS and mosquitto as MQTT broker. For all the previous steps, please have read to the articles in the ‘Links’ section at the end of this article.

Mosquitto Broker Setup

I’m running the mosquitto broker on my local machine with the following configuration:

port 8883
cafile C:\Program Files (x86)\mosquitto\certs\m2mqtt_ca.crt
certfile C:\Program Files (x86)\mosquitto\certs\m2mqtt_srv.crt
keyfile C:\Program Files (x86)\mosquitto\certs\m2mqtt_srv.key
tls_version tlsv1.

And running it with

mosquitto -c mosquitto.conf -v

Which starts it listening on port 8883 with TSL enabled:

C:\Program Files (x86)\mosquitto>mosquitto -c mosquitto.conf -v
1492938937: mosquitto version 1.4.11 (build date 20/02/2017 23:24:29.40) starting
1492938937: Config loaded from mosquitto.conf.
1492938937: Opening ipv6 listen socket on port 8883.
1492938937: Opening ipv4 listen socket on port 8883.

Mosquitto Client and Server Certificate Usage

I’m running the broker with the following server certificate setting:

certfile C:\Program Files (x86)\mosquitto\certs\m2mqtt_srv.crt

Using the mosquitto client to subscribe, I can subscribe to a topic with the following command line, using that same certificate:

mosquitto_sub -c -i MyMQTTclient -h localhost -p 8883 -q 0 -t HSLU/test -v --cafile c:\tmp\tls_ssl\client\m2mqtt_srv.crt --insecure

Notice that I have to specify the option –insecure. Without the –insecure it will give an error message like this:

1492934878: OpenSSL Error: error:14094438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error
1492934878: OpenSSL Error: error:140940E5:SSL routines:ssl3_read_bytes:ssl handshake failure
1492934878: Socket error on client <unknown>, disconnecting.

It took me while to find out why it is failing. A good way to test the TLS handshaking and connection is to use

openssl s_client -connect localhost:8883

which gives me the reason in the last line of the output:

-----END CERTIFICATE-----
subject=/C=CH/ST=Switzerland/L=Lucerne/O=HSLU/OU=T&A/CN=ErichStyger-PC/emailAddress=mail@hslu.ch
issuer=/C=CH/ST=Switzerland/L=Lucerne/O=HSLU/OU=T&A/CN=ErichStyger-PC/emailAddress=mail@hslu.ch
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1605 bytes and written 434 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: D241F3BFA8D26BDEE381353E2C517E46F8D04B48F307467A0E46FD7A2F3EB6BB
    Session-ID-ctx:
    Master-Key: <cut>
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - a0 fa a5 f5 8d 54 78 1a-7c 4e 86 51 4c 24 45 30   .....Tx.|N.QL$E0
    0010 - 97 44 de c1 fb c7 06 96-46 ed ef 27 67 c2 91 6f   .D......F..'g..o
    0020 - 40 38 ef 86 2a 12 59 cb-f0 60 0d 34 e6 be 2a ef   @8..*.Y..`.4..*.
    0030 - e5 7c c8 ee c3 ac cb 25-ef 63 49 3c 27 2e b0 3c   .|.....%.cI<'..<
    0040 - e3 a6 88 53 08 20 4b 53-2f 2b 6e 44 20 1a e7 24   ...S. KS/+nD ..$
    0050 - 60 a3 1a b0 08 74 74 56-46 13 22 0a 76 df 32 53   `....ttVF.".v.2S
    0060 - d7 b1 6b 82 63 34 fc c8-9c 2c a6 16 a2 73 75 9d   ..k.c4...,...su.
    0070 - 33 03 dc c7 db e0 c7 89-d0 49 ac fd 7d d3 33 0e   3........I..}.3.
    0080 - 35 eb df fc 05 b3 d0 bb-b7 02 25 67 86 71 76 f4   5.........%g.qv.
    0090 - 56 59 3b 39 2a dc 04 0e-e1 60 ae e4 17 1c 8f 62   VY;9*....`.....b
    00a0 - b9 bf f1 99 5e c5 15 3c-ae 60 60 cb 8e 63 1a af   ....^..<.``..c..

    Start Time: 1492935504
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)
---
closed

Ah, that makes sense: I have used a self-signed certificate :-). Now I know why I have to use the option –insecure with mosquitto_sub. I guess I could get rid of this with an non-self-signed certificate, but that’s too much of an effort for me now, as I’m only testing the connection.

Mosquitto Server and Subscriber

Mosquitto Server and Subscriber

mbed TLS with SSL Verification

To enable SSL verification and certificate verification, I have to replace

mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);

with:

ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *)mbedtls_m2mqtt_srv_crt, mbedtls_m2mqtt_srv_crt_len );
if(ret != 0) {
  printf( " failed\n  !  mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
}
mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL );
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);

For the call to mbedls_x509_crt_parse() I have to pass a pointer to the certificate string with its length. I have created the files certificate.h and certificate.h which holds my certificates. Certificate.h looks like this:

/*
 * certificate.h
 *
 *      Author: Erich Styger
 */

#ifndef CERTIFICATE_H_
#define CERTIFICATE_H_

#include <stddef.h> /* for size_t */

extern const char mbedtls_m2mqtt_srv_crt[];
extern const size_t mbedtls_m2mqtt_srv_crt_len;

#endif /* CERTIFICATE_H_ */

The certificate I have to use is the one from the broker/server (the m2mqtt_srv.crt I have created in “Enable Secure Communication with TLS and the Mosquitto Broker“) which is a file with the following content:

-----BEGIN CERTIFICATE-----
MIIDnjCCAoYCCQCUTXSPenGUCjANBgkqhkiG9w0BAQsFADCBkDELMAkGA1UEBhMC
Q0gxFDASBgNVBAgMC1N3aXR6ZXJsYW5kMRAwDgYDVQQHDAdMdWNlcm5lMQ0wCwYD
VQQKDARIU0xVMQwwCgYDVQQLDANUJkExFzAVBgNVBAMMDkVyaWNoU3R5Z2VyLVBD
MSMwIQYJKoZIhvcNAQkBFhRlcmljaC5zdHlnZXJAaHNsdS5jaDAeFw0xNzA0MjMw
ODAzNDdaFw0yNzA0MjEwODAzNDdaMIGQMQswCQYDVQQGEwJDSDEUMBIGA1UECAwL
U3dpdHplcmxhbmQxEDAOBgNVBAcMB0x1Y2VybmUxDTALBgNVBAoMBEhTTFUxDDAK
BgNVBAsMA1QmQTEXMBUGA1UEAwwORXJpY2hTdHlnZXItUEMxIzAhBgkqhkiG9w0B
CQEWFGVyaWNoLnN0eWdlckBoc2x1LmNoMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxGPL7lmqeDkq9Ah3DTieY0xQqV2ZoxSHUUpQ10e+A7bhNbumREdR
lQP1mHgaBX0IIiflNMHQ7WYZtrfFzVa09tguzN2oPGhRp0aamKRYsfOa1rTZVy31
zzHupKv+NlCi0LXqO1k8Tvvf/BbTa7GYB1hNwxdLc53rRZtxqveHUt5vywA6jpdu
x6SL9wmzFcspHJ+WOjfp09SOrLFAc9S3HsBwnK9TeDn7hVrR4I/6ne8mRLcCTtL1
qLUcGHWc2lAep5hyZBKepSVxC5KaqoHlAOyyMVYJyDhQhJ0W4ovCKjurK2rpUDIg
pjlxnCzNr6xtGXvScRFklF94PiH4x7O4DQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
AQBjjwlnK+Ej644ENcVwQpSf+0m8PbShbLFTsMNcP0rPLOcAJIZahNTlyMzzzb1F
pb7DxRUtBbcP735aA3zh+p279woV9DZjGLw4t0WMeFRAeJryoeTkhxHTBBJ/BFti
DEdOZaYeBMdyxCmO4WV9CzUFDXW5qYHoeTjLqfYAaY/nJiLN11d/MQoIsVXjg7PB
NBgD7SVK5WlbFR2kHyQzG9b3hNdCoax7S7yfkN6wFcIDKye2abEOi7VQOTEbQ2uk
AYj9dUTpo2d6T2xHvnPI69TSkt7cPp6uv5IlDn7856fnTARoF1LEGeWJRFNTYM2x
lzkg0fgI8eSV30YVrAHJIDOa
-----END CERTIFICATE-----

That certificate text gets translated into a ASCII array in certifcate.c (copy paste from the above file and ‘stringify’ it:

/*
 * certificate.c
 *
 *      Author: Erich Styger
 */

#include "certificate.h"

/* m2mqtt_srv.crt: see
 * https://mcuoneclipse.com/2017/04/14/enable-secure-communication-with-tls-and-the-mosquitto-broker/
 */

#define M2MQTT_CA_CRT_RSA                                                 \
"-----BEGIN CERTIFICATE-----\r\n"                                         \
"MIIDIDCCAggCCQCUTXSPenGUBzANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJD\r\n"    \
"SDEUMBIGA1UECAwLU3dpdHplcmxhbmQxEDAOBgNVBAcMB0x1Y2VybmUxDTALBgNV\r\n"    \
"BAoMBEhTTFUxDDAKBgNVBAsMA1QmQTAeFw0xNzA0MjMwNjQxMjdaFw0yNzA0MjEw\r\n"    \
"NjQxMjdaMFIxCzAJBgNVBAYTAkNIMRQwEgYDVQQIDAtTd2l0emVybGFuZDEQMA4G\r\n"    \
"A1UEBwwHTHVjZXJuZTENMAsGA1UECgwESFNMVTEMMAoGA1UECwwDVCZBMIIBIjAN\r\n"    \
"BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxGPL7lmqeDkq9Ah3DTieY0xQqV2Z\r\n"    \
"oxSHUUpQ10e+A7bhNbumREdRlQP1mHgaBX0IIiflNMHQ7WYZtrfFzVa09tguzN2o\r\n"    \
"PGhRp0aamKRYsfOa1rTZVy31zzHupKv+NlCi0LXqO1k8Tvvf/BbTa7GYB1hNwxdL\r\n"    \
"c53rRZtxqveHUt5vywA6jpdux6SL9wmzFcspHJ+WOjfp09SOrLFAc9S3HsBwnK9T\r\n"    \
"eDn7hVrR4I/6ne8mRLcCTtL1qLUcGHWc2lAep5hyZBKepSVxC5KaqoHlAOyyMVYJ\r\n"    \
"yDhQhJ0W4ovCKjurK2rpUDIgpjlxnCzNr6xtGXvScRFklF94PiH4x7O4DQIDAQAB\r\n"    \
"MA0GCSqGSIb3DQEBCwUAA4IBAQAFVuA5+QarBpT7U7w66JhrikxDTwfg/6Q7Guuo\r\n"    \
"AeuxpU0ida2UDqy3M31d5e/g906DbxctyeVERPXeDbivRFvJixGj2ry2mbjqHaYe\r\n"    \
"3OYGM2QEhfW3jSkd4IOzrxD5J3BEYLwtz7Yb/7w+CRO34f/5MlYQJWUZiM+IyFh4\r\n"    \
"R94rS3ietTK9AUnFTforc9GPBSjtagQVCdWoYo43+D2z/jDDIigkVhXL1ozJgsx/\r\n"    \
"DwoYYGjCqhommSthyzaQsIu4LyDZ0UZ4uUboTAl6yzmGjuO92TdmZl9wMi91dDfv\r\n"    \
"piwtIz+WkK31VWQYVtY8jIwowTuK6X5IBgodFv04/GYE6sWh\r\n"    \
"-----END CERTIFICATE-----\r\n"

const char mbedtls_m2mqtt_srv_crt[] = M2MQTT_CA_CRT_RSA;
const size_t mbedtls_m2mqtt_srv_crt_len = sizeof(mbedtls_m2mqtt_srv_crt);

Connection Log

With this I’m able to connect to the broker, start the TLS handshake with server certificate verification. I have implemented in the project a command line interface:

--------------------------------------------------------------
MQTT with lwip
--------------------------------------------------------------
CLS1                      ; Group of CLS1 commands
  help|status             ; Print help or status information
TmDt1                     ; Group of TmDt1 commands
  help|status             ; Print help or status information
  time [hh:mm:ss[,z]]     ; Set the current time. Prints the current time if no argument
  date [dd.mm.yyyy]       ; Set the current date. Prints the current date if no argument
  dateToSec <datetime>    ; Convert date/time int UNIX timestamp (seconds after 1970)
  secToDate <secs>        ; Convert UNIX timestamp to date/time
mqtt                      ; Group of mqtt commands
  help|status             ; Print help or status information
  connect                 ; Connect to the broker
  publish                 ; Publish topic to broker
  subscribe               ; Subscribe to topic on broker
  disconnect              ; Disconnect from the broker

Below the connection log:

CMD> mqtt connect
Initiating connection to broker...
CMD> Connecting to Mosquito broker
mqtt_client_connect: Connecting to host: 192.168.0.111 at port:8883
mqtt_tcp_connect_cb: TCP connection established to server, starting TLS handshake
mbedtls_net_send: len: 393
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 1358, free: 2738
mbedtls_net_recv: requested nof: 5, available 1358
mbedtls_net_recv: requested nof: 61, available 1353
mbedtls_net_recv: requested nof: 5, available 1292
mbedtls_net_recv: requested nof: 940, available 1287
mbedtls_net_recv: requested nof: 5, available 347
mbedtls_net_recv: requested nof: 333, available 342
mbedtls_net_recv: requested nof: 5, available 9
mbedtls_net_recv: requested nof: 4, available 4
mbedtls_net_send: len: 75
mbedtls_net_send: len: 6
mbedtls_net_send: len: 45
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 258, free: 3838
mbedtls_net_recv: requested nof: 5, available 258
mbedtls_net_recv: requested nof: 202, available 253
mbedtls_net_recv: requested nof: 5, available 51
mbedtls_net_recv: requested nof: 1, available 46
mbedtls_net_recv: requested nof: 5, available 45
mbedtls_net_recv: requested nof: 40, available 40
TLS handshake completed
mqtt_tcp_connect_cb: TCP connection established to server
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 23, get 0, put 23
mqtt_output_send: mbedtls_ssl_write: bytes 23
mbedtls_net_send: len: 52
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 33, free: 4063
mbedtls_net_recv: requested nof: 5, available 33
mbedtls_net_recv: requested nof: 28, available 28
mqtt_recv_from_tls: recv 4
mqtt_parse_incoming: Remaining length after fixed header: 2
mqtt_parse_incoming: msg_idx: 4, cpy_len: 2, remaining 0
mqtt_message_received: received CONNACK, Connect response code 0
mqtt_connection_cb: Successfully connected
Client is connected
mqtt_cyclic_timer: Sending keep-alive message to server
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 2, get 23, put 25
mqtt_output_send: mbedtls_ssl_write: bytes 2
mbedtls_net_send: len: 31
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 31, free: 4065
mbedtls_net_recv: requested nof: 5, available 31
mbedtls_net_recv: requested nof: 26, available 26
mqtt_recv_from_tls: recv 2
mqtt_parse_incoming: Remaining length after fixed header: 0
mqtt_message_received: Received PINGRESP from server
mqtt_cyclic_timer: Sending keep-alive message to server
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 2, get 25, put 27
mqtt_output_send: mbedtls_ssl_write: bytes 2
mbedtls_net_send: len: 31
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 31, free: 4065
mbedtls_net_recv: requested nof: 5, available 31
mbedtls_net_recv: requested nof: 26, available 26
mqtt_recv_from_tls: recv 2
mqtt_parse_incoming: Remaining length after fixed header: 0
mqtt_message_received: Received PINGRESP from server
mqtt_cyclic_timer: Sending keep-alive message to server
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 2, get 27, put 29
mqtt_output_send: mbedtls_ssl_write: bytes 2
mbedtls_net_send: len: 31
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 31, free: 4065
mbedtls_net_recv: requested nof: 5, available 31
mbedtls_net_recv: requested nof: 26, available 26
mqtt_recv_from_tls: recv 2
mqtt_parse_incoming: Remaining length after fixed header: 0
mqtt_message_received: Received PINGRESP from server

Below a publish request:

CMD> mqtt publish
Initiating PUBLISH to broker...
Publish to broker
mqtt_publish: Publish with payload length 1 to topic "HSLU/test"
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 14, get 31, put 45
mqtt_output_send: mbedtls_ssl_write: bytes 14
mbedtls_net_send: len: 43
Published to topic "HSLU/test", payload "3", res: 0
tls_tcp_sent_cb
mqtt_tcp_sent_cb: Calling QoS 0 publish complete callback

And a subscription request:

CMD> mqtt subscribe
Initiating SUBSCRIBE to broker...
Subscribe from broker
mqtt_sub_unsub: Client (un)subscribe to topic "HSLU/test", id: 2
mqtt_output_send: tcp_sndbuf: 8760 bytes, ringbuf_linear_available: 16, get 67, put 83
mqtt_output_send: mbedtls_ssl_write: bytes 16
mbedtls_net_send: len: 45
Suscribed to topic "HSLU/test", res: 0
tls_tcp_sent_cb
mbedtls_net_incoming: put nof bytes: 34, free: 4062
mbedtls_net_recv: requested nof: 5, available 34
mbedtls_net_recv: requested nof: 29, available 29
mqtt_recv_from_tls: recv 5
mqtt_parse_incoming: Remaining length after fixed header: 3
mqtt_parse_incoming: msg_idx: 5, cpy_len: 3, remaining 0
mqtt_message_received: SUBACK response with id 2
Subscribe result: 0

Code and Data Size

TLS and broker certificate handling does not come for free. Below is the code and data size of my application on the FRDM-K64F with lwip (no optimizations turned on in GCC). This is the base size (lwip, MQTT and without TLS):

 text data    bss    dec hex   filename
 104576 96 129776 234448 393d0 FRDM-K64F_lwip_mqtt_bm.axf

Adding the mbed TLS library adds around 150kBye of code size:

 text   data    bss    dec   hex filename
 351240 1008 136212 488460 7740c FRDM-K64F_lwip_mqtt_bm.axf

Adding the certificate handling is not that after that:

 text   data    bss    dec   hex filename
 355768 1008 136212 492988 785bc FRDM-K64F_lwip_mqtt_bm.axf

Summary

For debugging purposes it is fine to skip the server certificate verification. But for production code the server certificate needs to be verified to prevent ‘men in the middle attacks’. Moving from ‘no certificate verification’ to the ‘verify the certificate’ requires parsing and using the server certificate. This can be accomplished with a few calls to the mbed TLS library and does not add that much code size compared to the overall mbed library, but greatly extends security.

The updated project (MCUXpresso IDE with GNU tools, NXP FRDM-K64F board with lwip, MQTT and embedTLS) is available on GitHub: https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/MCUXpresso/FRDM-K64F/FRDM-K64F_lwip_lwip_mqtt_bm

Happy Certificating 🙂

Links

 

 

 

Advertisements

3 thoughts on “Tuturial: mbedTLS SSL Certificate Verification with Mosquitto, lwip and MQTT

  1. Pingback: Tutorial: Secure TLS Communication with MQTT using mbedTLS on top of lwip | MCU on Eclipse

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s