Adding TLS validation to an ESP32 project is an important step towards a more secure connection.
In this post, we will open a secure WebSocket connection and use a CA certificate to verify the identity of the remote server.
For this example, we will connect the ESP32 to echo.websocket.org, a public WebSocket echo server commonly used for testing.
Requirements
You will need:
-
SSID – your Wi-Fi network name
-
Password – the corresponding Wi-Fi password
-
Root CA certificate used by
echo.websocket.org
Getting the Root Certificate
We can obtain the root certificate used in the TLS chain by using OpenSSL.
Run the following command:
openssl s_client -showcerts -connect echo.websocket.org:443
This command prints the full TLS certificate chain presented by the server.
The output will contain several certificates in PEM format, like this:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
The last certificate in the chain usually corresponds to the Root CA.
We will use this certificate in the Arduino script.
Windows users
OpenSSL is not available natively on Windows. A convenient option is to install Git for Windows, which includes OpenSSL inside Git Bash. You can then run the command above directly from the Git Bash terminal.
Alternatively, you can refer to this post where I explain how to collect the certificate trust chain of a website using a Python script.
Getting the Complete SSL Trust Chain of a Website
Arduino code
Before uploading, replace the following values:
-
SSID – your Wi-Fi network name
-
Password – the corresponding Wi-Fi password
-
Root CA certificate used by
echo.websocket.org
#include <WiFi.h>
#include <WebSocketsClient.h>
const char* ssid = "******";
const char* password = "******";
const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
******
-----END CERTIFICATE-----
)EOF";
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_CONNECTED:
Serial.println("Connected to server");
webSocket.sendTXT("Hello from ESP32");
break;
case WStype_TEXT:
Serial.print("Received: ");
Serial.println((char*)payload);
break;
case WStype_DISCONNECTED:
Serial.println("Disconnected");
break;
}
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println("WiFi connected");
webSocket.beginSslWithCA("echo.websocket.org", 443, "/", ca_cert);
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}
If the certificate chain presented by the server matches the trusted CA, the connection is accepted. Otherwise, the connection will be rejected.
This process ensures that the ESP32 is communicating with the expected server and helps protect the device against man-in-the-middle attacks.
Expected Output
WiFi connected
Connected to server
Received: Request served by ...
Received: Hello from ESP32
ESP32