It's easier to see if you run that create_cert.sh
script, but there's a simple bug in it. Find it first. In the shell, go to the libraries
folder in your sketchbook. The default location of the sketchbook depends on your OS, but it's something like ~/Documents/Arduino
. The script is also a Bash script and uses utilities like openssl
and xxd
, so if you're on Windows, you need to figure that out. (Running WSL is one solution; you can link your mounted C:
drive into there.)
$ cd ~/Documents/Arduino/
$ cd libraries/HTTPS_Server_Generic/extras/
$ vi create_cert.sh
(Or whatever editor you use.) Lines 69-70
# Copy files to every example
for D in ../examples/*; do
At some point all the example .ino
files got organized into subdirectories and moved down one level, so it should be
for D in ../examples/*/*; do
Save and exit. The downloaded library files are not executable by default, so enable it and run it
$ chmod +x create_cert.sh
$ ./create_cert.sh
Generating RSA private key, 1024 bit long modulus
...
Adding certificate to example Static-Page
Adding certificate to example Websocket-Chat
Certificates created!
---------------------
Private key: private_key.h
Certificate data: cert.h
Now open an example, like HTTPS_Server_Generic > ESP32_WiFi > Static-Page, and examine the cert.h
file. It should now start with something like
#ifndef CERT_H_
#define CERT_H_
unsigned char example_crt_DER[] = {
0x30, 0x82, 0x02, 0x18, 0x30, 0x82, 0x01, 0x81, 0x02, 0x01, 0x02, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
If you examine the script file, it
- creates a fake CA (Certificate Authority)
- creates a new key pair
- signs the pair with the CA
- converts the resulting PEM (text) files to binary DER format
- generates the
.h
files, using xxd
to convert the bytes into a C statement
- runs that (fixed) loop to copy the results into the examples
One last wrinkle: the library requires OpenSSL. If you don't have it, it will fail when compiling because in a few places, it has
#include "openssl/ssl.h"
The current arduino-esp32
v3 board platform does not have it. But for example, my vendor-specific M5Stack platform running off a fork of v2 does. (The board platform files are off the OS-specific Arduino15
directory; this is a Mac)
$ cd ~/Library/Arduino15/packages/
$ find . -name ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32c3/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32c6/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32h2/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32s3/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./esp32/tools/esp32-arduino-libs/idf-release_v5.1-33fbade6/esp32s2/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32c3/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32c3/include/openssl/include/openssl/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32/include/openssl/include/openssl/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32s3/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32s3/include/openssl/include/openssl/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32s2/include/mbedtls/mbedtls/include/mbedtls/ssl.h
./m5stack/hardware/esp32/2.1.2/tools/sdk/esp32s2/include/openssl/include/openssl/ssl.h
If you scroll to the right, esp32
has mbedtls
, but not openssl
; m5stack
has both. Can you substitute one for the other? I have no idea.
If you add the usual WiFi credentials to the Static-Page example and Upload it, it should print something like
HTTPS WiFiWebServer is @ IP : 192.168.1.15
To access, use https://192.168.1.15
Starting server...
Server ready.
As mentioned in a few places, the self-signed cert cannot be validated, but it otherwise fulfills all the requirements. So if you use a browser, you'll get a warning. And if you use curl
, you'll need to use the --insecure
switch, which is also -k
(-i
adds the status-line and headers)
$ curl -i https://192.168.1.15
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
$ curl -ik https://192.168.1.15
HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>
<head><title>Hello World!</title></head>
<body>
<h1>Hello World!</h1>
<p>Your server is running for 35 seconds.</p>
</body>
</html>
So why do you need HTTPS at all? If you want to ensure that the server is not being spoofed, then you when you get the key pair files, they'll be either in PEM or DER format. You can use the same commands in create_cert.sh
to convert those into the .h
files.
Let's Encrypt is free; there are others.
But if you just need the connection to be encrypted, then the clients usually have a way of saying: don't bother validating. ESP32 NetworkClientSecure
/WiFiClientSecure
has setInsecure
, like curl
; you just turn it off. Knowing the risks, that may be acceptable for personal use.