NodeMCU/ESP8266 and SSL/TLS/HTTPS

Security is always good. But you need to pay attention to implement it well, otherwise it can be very harmful.

Let's say that we want to secure the communication between our NodeMCU (ESP8266) and a server (cloud/LAN/whatever). Starting from NodeMCU devkit 2.4, you have a class named WiFiClientSecure, which implements a TLSv1.2 client with some recent (and secure) ciphers, such as aes_128_cbc_sha256.

Trust me, you want to use this class. Do not try to implement another TLS class if you don't know what you're doing.

Before seek for some examples, let's clear some points:

Checking the certificate fingerprint

Each certificate has its own fingerprint. It's hard to match with a (valid) private key, so checking the fingerprint is a valid method in order to secure a channel.

Pros:

Cons:

This method is useful on self-signed certificates or with a certificate that is valid far beyond the software version lifetime, because you cannot generate two certificates with the same fingerprint (this means, for example, that you cannot use Let's encrypt certificates, as they expires after 90 days, unless you're planning to release a new version every 90 days).

Code example:

WiFiClientSecure client;
int result = client.connect("tls.host", 443);
if (result != 1) {
  // Something bad happened on connection
} else {
  // Socket is connected BUT the certificate is not verified yet
  // Verifying the certificate by using fingerprint
  if(client.verify("D1:D0: ... :7E, "tls.host")) {
    // Certificate is OK
  } else {
    // Certificate is invalid
  }
}

Checking the certificate trust chain

This method is the most widely used because of its versatility: the client checks for a signature from a trusted certification authority (from a local storage of certificates). So, even if the certificate changes, as long as it's signed by a trusted CA, the certificate is valid.

WiFiClientSecure doesn't handle a CA bundle file (yet), so if you want to check multiple certification authorities, you need to load them manually and check each of them. In the example, we'll see how to check only for one CA: it's trivial to convert it in a loop for checking multiple certificates.

Pros:

Cons:

Code example (in the example, ca_cert is the CA certificate binary loaded in memory, and ca_cert_len is the length of ca_cert memory area, in bytes):

WiFiClientSecure client;
int result = client.connect("tls.host", 443);
if (result != 1) {
  // Something bad happened on connection
} else {
  // Socket is connected BUT the certificate is not verified yet
  // Load CA certificate
  client.setCACert(ca_cert, ca_cert_len);
  // Verifying the certificate by using loaded certificate
  if(client.verifyCertChain("tls.host")) {
    // Certificate is OK
  } else {
    // Certificate is invalid
  }
}