TCP connection SSL support
Warning
SSL support is currently in experimental mode. API changes may occur in the future.
ESP-AT binary, running on Espressif chips, supports SLL connection types. Such connections, to work properly, require client or server certificates to be loaded to Espressif device.
With the recent update, July 29th, 2023, library has been updated to support AT commands for flash and MFG operations, allowing host microcontroller to load required certificates to the Espressif device.
Note
Minimum required ESP-AT library running on ESP device is now v3.2.0
, which supports new AT+SYSMFG command,
that is required to load custom data to the device.
Note
SSL connections mentioned on this page are secure from Espressif device towards network. Data between host MCU and Espressif MCU is not protected and may be exposed to an attacker
Prepare the certificate
Assuming we would like to establish connection to another server with secure SSL connection,
ESP device shall have up to 3
certificates loaded in its own system flash. These are:
client_ca - Client root CA certificate - client uses this certificate to verify server. Example
client_cert - Client certificate. Example
client_key - Client private key. Example
ESP-AT website includes some test certificates, that could be used for test purposes: Description of all slots
LwESP repository contains aforementioned certificates in the certificates folder. There are 2 files for each certificate:
Original .crt or .key file
Original .crt or .key file converted to .hex array for easier include in the C project.
Load to ESP device
Loading can be done as part of custom AT firmware build, or by using AT commands. LwESP library has the support for system flash and manufacturing NVS data operation, that is required for certificate load.
All combined, steps to establish SSL connection is to:
Have certificates loaded to ESP-AT device with Espressif format
Have configured connections to use your certificates, if SSL type is used on them
Have valid time in ESP device. SNTP module can help with that
Example
Below is the up-to-date netconn API example using SSL connection. Example file is located in snippets/netconn_client_ssl.c
1/*
2 * Netconn client demonstrates how to connect as a client to server
3 * using sequential API from separate thread.
4 *
5 * it does not use callbacks to obtain connection status.
6 *
7 * Demo connects to NETCONN_HOST at NETCONN_PORT and sends GET request header,
8 * then waits for respond and expects server to close the connection accordingly.
9 */
10#include "lwesp/lwesp.h"
11#include "lwesp/lwesp_netconn.h"
12#include "netconn_client.h"
13
14/* Certificates, ready to be loaded to the flash */
15static uint8_t client_ca[] = {
16#include "../certificates/client_ca_generated_atpki.hex"
17};
18static uint8_t client_cert[] = {
19#include "../certificates/client_cert_generated_atpki.hex"
20};
21static uint8_t client_key[] = {
22#include "../certificates/client_key_generated_atpki.hex"
23};
24
25/**
26 * \brief Host and port settings
27 */
28#define NETCONN_HOST "example.com"
29#define NETCONN_PORT 443
30
31/**
32 * \brief Request header to send on successful connection
33 */
34static const char request_header[] = ""
35 "GET / HTTP/1.1\r\n"
36 "Host: " NETCONN_HOST "\r\n"
37 "Connection: close\r\n"
38 "\r\n";
39
40/**
41 * \brief Netconn client thread implementation
42 * \param[in] arg: User argument
43 */
44void
45netconn_client_ssl_thread(void const* arg) {
46 lwespr_t res;
47 lwesp_pbuf_p pbuf;
48 lwesp_netconn_p client;
49 lwesp_sys_sem_t* sem = (void*)arg;
50
51 /* Make sure we are connected to access point first */
52 while (!lwesp_sta_has_ip()) {
53 lwesp_delay(1000);
54 }
55
56 /*
57 * First create a new instance of netconn
58 * connection and initialize system message boxes
59 * to accept received packet buffers
60 */
61 client = lwesp_netconn_new(LWESP_NETCONN_TYPE_SSL);
62 if (client != NULL) {
63 struct tm dt;
64 uint32_t sntp_interval = 0;
65 uint8_t sntp_en = 0;
66
67 /* Write data to coresponding manuf NVS */
68 res = lwesp_mfg_write(LWESP_MFG_NAMESPACE_CLIENT_CA, "client_ca.0", LWESP_MFG_VALTYPE_BLOB, client_ca,
69 sizeof(client_ca), NULL, NULL, 1);
70 res = lwesp_mfg_write(LWESP_MFG_NAMESPACE_CLIENT_CERT, "client_cert.0", LWESP_MFG_VALTYPE_BLOB, client_cert,
71 sizeof(client_cert), NULL, NULL, 1);
72 res = lwesp_mfg_write(LWESP_MFG_NAMESPACE_CLIENT_KEY, "client_key.0", LWESP_MFG_VALTYPE_BLOB, client_key,
73 sizeof(client_key), NULL, NULL, 1);
74
75 /* Configure SSL for all connections */
76 for (size_t i = 0; i < LWESP_CFG_MAX_CONNS; ++i) {
77 lwesp_conn_ssl_set_config(i, 1, 0, 0, NULL, NULL, 1);
78 }
79
80 /* Ensure SNTP is enabled, time is required for SSL */
81 if (lwesp_sntp_get_config(&sntp_en, NULL, NULL, NULL, NULL, NULL, NULL, 1) == lwespOK) {
82 if (!sntp_en) {
83 lwesp_sntp_set_config(1, 2, NULL, NULL, NULL, NULL, NULL, 1);
84 }
85 lwesp_sntp_get_interval(&sntp_interval, NULL, NULL, 1);
86 printf("SNTP interval: %u seconds\r\n", (unsigned)sntp_interval);
87 do {
88 lwesp_sntp_gettime(&dt, NULL, NULL, 1);
89 if (dt.tm_year > 100) {
90 break;
91 }
92 lwesp_delay(1000);
93 } while (1);
94 }
95
96 /*
97 * Connect to external server as client
98 * with custom NETCONN_CONN_HOST and CONN_PORT values
99 *
100 * Function will block thread until we are successfully connected (or not) to server
101 */
102 res = lwesp_netconn_connect(client, NETCONN_HOST, NETCONN_PORT);
103 if (res == lwespOK) { /* Are we successfully connected? */
104 printf("Connected to " NETCONN_HOST "\r\n");
105 res = lwesp_netconn_write(client, request_header, sizeof(request_header) - 1); /* Send data to server */
106 if (res == lwespOK) {
107 res = lwesp_netconn_flush(client); /* Flush data to output */
108 }
109 if (res == lwespOK) { /* Were data sent? */
110 printf("Data were successfully sent to server\r\n");
111
112 /*
113 * Since we sent HTTP request,
114 * we are expecting some data from server
115 * or at least forced connection close from remote side
116 */
117 do {
118 /*
119 * Receive single packet of data
120 *
121 * Function will block thread until new packet
122 * is ready to be read from remote side
123 *
124 * After function returns, don't forgot the check value.
125 * Returned status will give you info in case connection
126 * was closed too early from remote side
127 */
128 res = lwesp_netconn_receive(client, &pbuf);
129 if (res
130 == lwespCLOSED) { /* Was the connection closed? This can be checked by return status of receive function */
131 printf("Connection closed by remote side...\r\n");
132 break;
133 } else if (res == lwespTIMEOUT) {
134 printf("Netconn timeout while receiving data. You may try multiple readings before deciding to "
135 "close manually\r\n");
136 }
137
138 if (res == lwespOK && pbuf != NULL) { /* Make sure we have valid packet buffer */
139 /*
140 * At this point, read and manipulate
141 * with received buffer and check if you expect more data
142 *
143 * After you are done using it, it is important
144 * you free the memory, or memory leaks will appear
145 */
146 printf("Received new data packet of %d bytes\r\n", (int)lwesp_pbuf_length(pbuf, 1));
147 lwesp_pbuf_free_s(&pbuf); /* Free the memory after usage */
148 }
149 } while (1);
150 } else {
151 printf("Error writing data to remote host!\r\n");
152 }
153
154 /*
155 * Check if connection was closed by remote server
156 * and in case it wasn't, close it manually
157 */
158 if (res != lwespCLOSED) {
159 lwesp_netconn_close(client);
160 }
161 } else {
162 printf("Cannot connect to remote host %s:%d!\r\n", NETCONN_HOST, NETCONN_PORT);
163 }
164 lwesp_netconn_delete(client); /* Delete netconn structure */
165 }
166
167 printf("Terminating thread\r\n");
168 if (lwesp_sys_sem_isvalid(sem)) {
169 lwesp_sys_sem_release(sem);
170 }
171 lwesp_sys_thread_terminate(NULL); /* Terminate current thread */
172}