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.
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.
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
- MQTT with lwip and NXP FRDM-K64F Board
- Introduction to Security and TLS (Transport Layer Security)
- Enable Secure Communication with TLS and the Mosquitto Broker
- Tutorial: Secure TLS Communication with MQTT using mbedTLS on top of lwip
- TLS Security: https://dzone.com/articles/tls-security-tlsssl-explained
- TLS and web certificates explained: https://dzone.com/articles/a-look-at-tls-transport-layer-security
- Understanding security in IoT: https://dzone.com/articles/understanding-security-in-iot-ssltls
- Understanding Transport Layer Security: https://dzone.com/articles/understanding-transport-layer
Pingback: Tutorial: Secure TLS Communication with MQTT using mbedTLS on top of lwip | MCU on Eclipse
Typo in the title? “SSL Certificate”
LikeLike
ups! Thanks for catching this!
LikeLike
Hi Erich,
for the connection log you said that you’ve implemented in this project a command line interface. How can I reach/make this command interface?
LikeLike
Connect with a console (38400 baud) to the OpenSDA virtual COM port. The application implements a command line shell.
LikeLike
Hi Erich,
first of all thanks for your past comments. They were really helpful.
Update my settings:
I’m connecting now my board Segger J-Link through the OpenSDA.
I connected a console (38400 baud) to the OpenSDA /dev/ttyACM0 /* because linux*/.
The Peripherals has been checked with only UART0 (38400 baud).
Problems:
I have only two problems and I don’t know if they hang from each other, anyway:
1- when I command in the terminal (e.g. CMD> help) then I get outputs just like above but they don’t look orderly, I mean the words and the lines.
2- In the Preocessor Expert/ Components/ e.g. CLS1:Shell, they could not be configured. When I selct one of them then I get ” The component $name$ is not installed in this version of Processor Expert”.
I hope you can help.
LikeLike
1) what do you get here? It could be that you have a problem on your host?
2) That would be a problem. Do you have the latest components from SourceForge already (https://mcuoneclipse.com/2018/09/30/mcuoneclipse-components-30-sept-2018-release/)?
LikeLike
1- Yes! You’re right, I tried it in another host then it came normally.
2- No. I don’t have it! I installed it but how can I add it?
LikeLike
2: see https://mcuoneclipse.com/2018/09/30/mcuoneclipse-components-30-sept-2018-release/
LikeLike
Hi Erich,
your link hier https://mcuoneclipse.com/2014/10/21/mcuoneclipse-releases-on-sourceforge/ was very helpful to add the components.
However, I still have one problem to get it working:
In the Peripherals settings I get the warning “Peripheral UART0 is not initialized”.
My question:
Which settings should be made exactly for the Peripherals?
LikeLike
Hi Ahmed,
I’m really not sure from where this warning is coming from? It seems that you don’t correctly initialize the UART0. I don’t get that message from my project on GitHub.
LikeLike
Hi Erich,
Thank you for sharing your article.
Excuse me, I downloaded the underlying project from your github.
FRDM-K64F_lwip_lwip_mqtt_bm,
Then modify CONFIG_USE_FREERTOS in config.h and set it to 0.
After compiling, in lwip_mqtt.c, the problem of unknown type name ‘QueueHandle_t’ appears, because there is a dependency between the variable and the file, I don’t know how to solve it now, thank you!
LikeLike
That project started as a bare metal project, but then evolved into one using FreeRTOS. I checked the settings, but unfortunately it is not easy to get it back to a bare metal project.
Actually running the lwip stack without an RTOS is not a good thing anyway in my view, as the RTOS makes things much easier to handle.
But I have added a note to the project to make others aware.
Thanks for reporting!
LikeLike