ESP32 Arduino HTTPS webserver example using certificate from LittleFS

The following example reads a HTTPS/TLS certificate from LittleFS (cert.pem and privkey.pem – these are just standard X.509 PEM files) and uses those to initialize a HTTPS server. Most of this example is based on the esp-idf https_server example.

Our code to initialize the HTTPS server uses code from the following posts:

The core initialization sequence works like this:

// These need to be GLOBAL variables, they still need to exist
// after initialization!
std::string cert;
std::string privkey;

httpd_ssl_config_t conf = HTTPD_SSL_CONFIG_DEFAULT();
httpd_handle_t server = nullptr;

void InitializeHTTPServer() {
        std::string cert = ReadFileToString("/cert.pem");
        std::string privkey = ReadFileToString("/privkey.pem");

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }
}

However it is typically far more convenient to use our HTTPSServer class:

class HTTPSServer {
public:

    HTTPSServer(): conf(HTTPD_SSL_CONFIG_DEFAULT()) {
    }

    void ReadCertificateInfoFromFilesystem() {
        cert = ReadFileToString("/cert.pem");
        privkey = ReadFileToString("/privkey.pem");
    }

    void StartServer() {
        // Start the httpd server
        ESP_LOGI(TAG, "Starting server");

        ReadCertificateInfoFromFilesystem();

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;

    httpd_ssl_config_t conf;
    std::string cert;
    std::string privkey;
};

Usage example:

void setup() {
    InitFilesystem();

    // TODO setup wifi network

    httpsServer.StartServer();
    // Register your 
    httpsServer.RegisterHandler(&root);
}

Full example:

#include <Arduino.h>
#include <WiFi.h>
#include <LittleFS.h>
#include <string>
#include <esp_https_server.h>
#include "esp_tls.h"

//AsyncWebServer server(80);
static const char *TAG = "https-littlefs-example";

volatile bool filesystemOK = false;

void InitFilesystem() {
  // Initialize LittleFS
  if (!LittleFS.begin(false /* false: Do not format if mount failed */)) {
    Serial.println("Failed to mount LittleFS");
    if (!LittleFS.begin(true /* true: format */)) {
      Serial.println("Failed to format LittleFS");
    } else {
      Serial.println("LittleFS formatted successfully");
      filesystemOK = true;
    }
  } else { // Initial mount success
    filesystemOK = true;
  }
}

/* An HTTP GET handler */
static esp_err_t root_get_handler(httpd_req_t *req)
{
    httpd_resp_set_type(req, "text/html");
    httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);

    return ESP_OK;
}

static const httpd_uri_t root = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = root_get_handler
};

size_t LittleFSFilesize(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Don't forget to clean up!
  file.close();
  return filesize;
}

std::string ReadFileToString(const char* filename) {
  auto file = LittleFS.open(filename, "r");
  size_t filesize = file.size();
  // Read into temporary Arduino String
  String data = file.readString();
  // Don't forget to clean up!
  file.close();
  return std::string(data.c_str(), data.length());
}

class HTTPSServer {
public:

    HTTPSServer(): conf(HTTPD_SSL_CONFIG_DEFAULT()) {
    }

    void ReadCertificateInfoFromFilesystem() {
        cert = ReadFileToString("/cert.pem");
        privkey = ReadFileToString("/privkey.pem");
    }

    void StartServer() {
        // Start the httpd server
        ESP_LOGI(TAG, "Starting server");

        ReadCertificateInfoFromFilesystem();

        conf.cacert_pem = (const uint8_t*)this->cert.c_str();
        conf.cacert_len = this->cert.size() + 1;

        conf.prvtkey_pem = (const uint8_t*)this->privkey.c_str();
        conf.prvtkey_len = this->privkey.size() + 1;

        esp_err_t ret = httpd_ssl_start(&server, &conf);
        if (ESP_OK != ret) {
            ESP_LOGI(TAG, "Error starting server!");
            return;
        }

        // Set URI handlers
        ESP_LOGI(TAG, "Registering URI handlers");
        httpd_register_uri_handler(server, &root);
        return;
    }

    void RegisterHandler(const httpd_uri_t *uri_handler) {
        httpd_register_uri_handler(this->server, uri_handler);
    }

    httpd_handle_t server = nullptr;

    httpd_ssl_config_t conf;
    std::string cert;
    std::string privkey;
};

HTTPSServer httpsServer;

void setup() {
    Serial.begin(115200);
    delay(500);

    InitFilesystem();

    // Connect Wifi, restart if not connecting
    // https://techoverflow.net/2021/01/21/how-to-fix-esp32-not-connecting-to-the-wifi-network/
    WiFi.begin("MyWifi", "Lobae1tie5achokef8riequohgohx");

    uint32_t notConnectedCounter = 0;
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
        Serial.println("Wifi connecting...");
        notConnectedCounter++;
        if(notConnectedCounter > 150) { // Reset board if not connected after 5s
            Serial.println("Resetting due to Wifi not connecting...");
            ESP.restart();
        }
    }
    Serial.print("Wifi connected, IP address: ");
    Serial.println(WiFi.localIP());

    httpsServer.StartServer();
    httpsServer.RegisterHandler(&root);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}
[env:esp32dev]
platform = espressif32
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.5
board = esp32dev
framework = arduino
board_build.filesystem = littlefs
monitor_speed = 115200