ESP32: HTTPS web server

In this tutorial we will check how to setup a HTTPS web server on the ESP32, using the Arduino core. For testing purposes, we will use a self signed certificate, generated on the fly by the ESP32. The tests shown on this tutorial were performed using an ESP32 board from DFRobot.

Introduction

In this tutorial we will check how to setup a HTTPS web server on the ESP32, using the Arduino core. For testing purposes, we will use a self signed certificate, generated on the fly by the ESP32.

For this tutorial we will use this library. You can check the installation instructions here. In short, we simply need to download the content of the repository, unzip it and place it in the Arduino libraries folder of our computer.

Then, after restarting the Arduino IDE, the library should become available for importing and its examples should be available for testing.

The tutorial shown below is based on this example from the library, which I really encourage you to try. You can check here the full list of examples, which are very well explained.

The tests shown on this tutorial were performed using an ESP32 board from DFRobot.

The code

We will start the by doing the library includes. Naturally, we will need the WiFi.h library, so we can connect the ESP32 to a WiFi network.

#include <WiFi.h>

Then, we will need a couple more includes to be able to setup the web server. We are not going to analyze in detail what is contained in each header file, but you can check their content here.

#include <HTTPSServer.hpp>
#include <SSLCert.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>

Additionally, we will declare the use of the httpsserver namespace, so it becomes easier to use in the code below.

using namespace httpsserver;

Since we will connect the ESP32 to a WiFi network, we will need to declare its credentials, more precisely the network name and password.

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

Then, we will need a pointer to an object of class SSLCert, which will hold the information of our server certificate. We will generate the certificate below, in the Arduino setup function.

SSLCert * cert;

Additionally, we will need a pointer to an object of class HTTPSServer, which we will use to setup the secure server. In particular, we will use this object later to register the routes of our server.

HTTPSServer * secureServer;

Moving on to the Arduino setup function, we will start by opening a serial connection, so we can output some content from our program.

Serial.begin(115200);

Then, we will create an instance of the SSLCert class. Note that we will be using the new operator, which means it will return a pointer to the created object.

cert = new SSLCert();

After this, we will generate the self signed certificate. We will do this with a call to the createSelfSignedCert function, which receives the following parameters:

  • A reference to our SSLCert object;
  • An enumerated value of type SSLKeySize, which represents the size of the key to be used by the certificate. We will use a key of 2048 bits, so we pass the value KEYSIZE_2048;
  • A string with the distinguished name (DN) of the certificate, following the x509 specifications [2]. We will use the example suggested on the header file where the function is defined. You can consult here the meaning of the attributes used in the example.

This function will return an integer with the value 0 in case the generation of the certificate went well, as can be confirmed in this file. We will store that value in a variable so we can perform an error check.

Note that the procedure to generate the self signed certificate may take a while.

int createCertResult = createSelfSignedCert(
    *cert,
    KEYSIZE_2048,
    "CN=myesp.local,O=acme,C=US");

if (createCertResult != 0) {
    Serial.printf("Error generating certificate");
    return; 
}

After this, we will create an object of class HTTPSServer, passing as input our self signed SSLCert object pointer.

By default, the server will listen for incoming requests on port 443. Since this is an optional parameter of the constructor, we can change it if we want. Nonetheless, for this tutorial, we will keep the value 443, since it is the default HTTPS port.

secureServer = new HTTPSServer(cert);

After this, we will connect the ESP32 to the WiFi network, using the previously declared credentials, stored in global variables. After the procedure finishes, we will print to the serial port the IP address assigned to the ESP32, which will be needed for the client to reach the server.

WiFi.begin(ssid, password);
 
while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
}
 
Serial.println(WiFi.localIP());

Now we need to configure the routes of our server. In our case, since we are developing a very simple testing example focused on the generation of the certificate, we will simply have the index route (“/“).

In this library implementation, all the routes that belong to the server need to be specified as a Resource Node [3]. Basically, a Resource Node will link a handler function to a route and a HTTP method [3], pretty much like we have been doing on other HTTP web server frameworks for the ESP32.

So, the constructor of the ResourceNode class receives the following inputs:

  • A string with the path of the route. In our case, it will be “/“, since we will be configuring the index route;
  • A string with the HTTP method supported by the route. In our case, it will be “GET“;
  • A route handling function, which should return void and receive as parameters a pointer to an object of class HTTPRequest and a pointer to an object of class HTTPResponse. To make the code shorter, we will make use of the C++ lambda syntax do declare this function anonymously.
ResourceNode * nodeRoot = new ResourceNode("/", "GET", [](HTTPRequest * req, HTTPResponse * res){
    // Implementation of the route handling function
});

In the implementation of the route handling function, we will make use of the HTTPResponse object pointer to return an answer back to the client.

To do it, we simply need to call the println method on the mentioned object, passing as input a string with the content we want to send back to the client.

Note that, by default, a 200 status code will be returned to the client, which means we don’t need any additional code for our simple example.

ResourceNode * nodeRoot = new ResourceNode("/", "GET", [](HTTPRequest * req, HTTPResponse * res){
    res->println("Secure Hello World!!!");
});

After this, we need to register the node on our server. To do it, we need to call the registerNode method on our server object, passing as input the pointer to the node we have just created.

secureServer->registerNode(nodeRoot);

Finally, to start the server, we simply need to call the start method on our server object. To make sure everything started fine, we can call the isRunning method afterwards, which returns a Boolean value indicating if the server is running (true) or not (false).

secureServer->start();

if (secureServer->isRunning()) {
    Serial.println("Server ready.");
}

Now that we have setup the server, we just need to periodically call the loop method on our server object, to handle client connections. This can be done in the Arduino main loop, with a small delay between each method call.

Naturally, in order to optimize this, we can simply launch a dedicated FreeRTOS task to handle the loop method call asynchronously. You can check an example on how to do it here.

void loop() {
 
  secureServer->loop();
 
  delay(10);
}

The final code can be seen below. It contains some additional prints to give feedback to the user before and after the creation of the certificate, and after the server is running.

#include <WiFi.h>
#include <HTTPSServer.hpp>
#include <SSLCert.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>

using namespace httpsserver;

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

SSLCert * cert;
HTTPSServer * secureServer;

void setup() {

  Serial.begin(115200);

  Serial.println("Creating certificate...");
  
  cert = new SSLCert();

  int createCertResult = createSelfSignedCert(
    *cert,
    KEYSIZE_2048,
    "CN=myesp.local,O=acme,C=US");
  
  if (createCertResult != 0) {
    Serial.printf("Error generating certificate");
    return; 
  }

  Serial.println("Certificate created with success");
  
  secureServer = new HTTPSServer(cert);

 
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }
 
  Serial.println(WiFi.localIP());


  ResourceNode * nodeRoot = new ResourceNode("/", "GET", [](HTTPRequest * req, HTTPResponse * res){
    res->println("Secure Hello World!!!");
  });

  secureServer->registerNode(nodeRoot);

  secureServer->start();
  
  if (secureServer->isRunning()) {
    Serial.println("Server ready.");
  }
}

void loop() {
 
  secureServer->loop();
 
  delay(10);
}

Testing the code

To test the code, simply compile it and upload it to your ESP32, using the Arduino IDE. Once the procedure finishes, open the IDE serial monitor.

The device should start by generating the certificate, which may take a while, as already mentioned. After that, it will connect to the WiFi network and then print the IP address assigned to it on the network. At the end, you should have a result similar to figure 1.

Output of the Arduino program on the Serial Monitor, after generating self signed certificate and connecting to WiFi with the ESP32
Figure 1 – Output of the program, after the generation of the self signed certificate and the connection to the WiFi network.

After this, copy the IP address that was printed on the serial monitor. Then, open a web browser of your choice and paste the following in the address bar, changing #yourDeviceIp# by the IP you have just copied:

https://#yourDeviceIp#/

After navigating to the previous URL, your browser should warn you about some security issue with the certificate. This happens because the server is using a self signed certificate, meaning it wasn’t issued by a Certification Authority that the browsers knows and trusts. You can ignore this warning and choose to proceed.

After proceeding, you should go to a page similar to figure 2, which shows the message we have defined in the Arduino code.

In my case I’m using Google Chrome. If we access the Security tab of this web browser, we can see that some problems with the certificate are reported (due to the fact that it is self signed), but it indicates that the connection is encrypted and the resources are served securely, as expected when using HTTPS.

Accessing the content served by the ESP32 over HTTPS
Figure 2 – Accessing the content served by the ESP32 over HTTPS.

Related Posts

References

[1] http://www.cplusplus.com/reference/new/operator%20new/

[2] https://github.com/fhessel/esp32_https_server/blob/a55066bd22547b25f5ba2fd0e444e6bf611917ab/src/SSLCert.hpp

[3] https://github.com/fhessel/esp32_https_server#add-resources-to-the-server

2 Replies to “ESP32: HTTPS web server”

  1. Any idea why I got the following error?
    In file included from D:\fling\Documents\Arduino\libraries\src/HTTPResponse.hpp:12:0,
    from D:\fling\Documents\Arduino\libraries\src/HTTPSCallbackFunction.hpp:5,
    from D:\fling\Documents\Arduino\libraries\src/ResourceNode.hpp:7,
    from D:\fling\Documents\Arduino\libraries\src/HTTPServer.hpp:20,
    from D:\fling\Documents\Arduino\libraries\src/HTTPSServer.hpp:15,
    from D:\fling\Documents\Hobby\HTTPS Server\esp32_https_server-master\examples\Self-Signed-Certificate\Self-Signed-Certificate.ino:26:
    C:\Users\fuyun_000\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.2/tools/sdk/include/openssl/openssl/ssl.h:395:27: error: macro “SSL_library_init” passed 1 arguments, but takes just 0
    void SSL_library_init(void);
    ^
    exit status 1
    Please help. Thanks in advance.

Leave a Reply