Chris Dzombak

HTTPS Requests with a Small Set of Root Certificates on ESP8266 + Arduino

In yesterday’s post, I walked through best practices for making secure HTTPS connections using a root certificate store on ESP8266 with Arduino. In that example, I used Mozilla’s full list of trusted root certificates for my certificate store.

But what if I know I only need to trust one or a few certificate authorities for my application?

In this post, I’ll walk through making HTTPS requests with just a few trusted root certificates. We’ll use the X509List class, which I discussed briefly in my previous post; and for this application we’ll trust only certificates issued by Let’s Encrypt.

You can find the full code for this example in the letsencrypt branch of my ESP8266 WiFi Demos repository.

1. Download Trusted Root Certificates

First, we’ll need to get the PEM-encoded versions of each root certificate we want to trust. (For more on certificate encodings, this forum post is provides an overview.)

We only want to trust Let’s Encrypt certificates, so we just need to get copies of their root certificates. This is easy; they have a page clearly documenting their chain of trust, including links to download the relevant certificates in multiple formats.

We’ll download the self-signed, pem-encoded version of each root certificate: ISRG Root X1 and ISRG Root X2.

The text from each of those files gets copied into a header we’ll include in our application. It looks something like:

const char cert_ISRG_X1 [] PROGMEM = R"CERT(
-----BEGIN CERTIFICATE-----
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
( ... CONTENTS SNIPPED FOR THIS PREVIEW ... )
-----END CERTIFICATE-----
)CERT";

const char cert_ISRG_X2 [] PROGMEM = R"CERT(
-----BEGIN CERTIFICATE-----
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
( ... CONTENTS SNIPPED FOR THIS PREVIEW ... )
-----END CERTIFICATE-----
)CERT";

See the complete file here.

(The PROGMEM directive tells the system to store this data in flash rather than in SRAM. See the Arduino documentation for details.)

2. Add Certificates to an X509List

We’ll define a global, shared X509List:

#include <BearSSLHelpers.h>
BearSSL::X509List trustedRoots;

And then, in our application’s setup() function, add the two root certificates to the X509List:

#include "certs.h"

void setup() {
    // ...
    trustedRoots.append(cert_ISRG_X1);
    trustedRoots.append(cert_ISRG_X2);
    // ...
}

3. Configure WiFiClientSecure to use the X509List

After adding the certificates to the X509List, also in setup(), we’ll tell our WiFiClientSecure to use the configured X509List:

wifiClient.setTrustAnchors(&trustedRoots);

4. Make HTTPS Requests

Refer to the “Making HTTPS Requests” section of yesterday’s post for details on this snippet; the code that actually makes HTTPS requests is unchanged.

#include <ESP8266HTTPClient.h>

    // ...
    HTTPClient httpClient;
    httpClient.begin(wifiClient, "https://ip.dzdz.cz");
    int respCode = httpClient.GET();
    if (respCode >= 400) {
        // HTTP error
    } else if (respCode > 0) {
        String result = httpClient.getString();
        // Do something with result...
    } else {
        // Other error occurred
    }
    httpClient.end();
    // ...

Putting it all together

As with yesterday’s post, this is pretty straightforward, and I encourage you to read over my example code. It’ll take less time than you spent reading this post, I promise!