WebServer with HTTPS connection

Hello,

I would like to configure my Portenta H7 with an Webserver that provides HTTPS connection.
The key and cert files are available and readable.
Unfortunatly I got the follwing error message:

Traceback (most recent call last):
File “”, line 64, in
File “ssl.py”, line 1, in wrap_socket
File “ssl.py”, line 1, in load_cert_chain
OSError: [Errno 2] ENOENT

I use the following code:

try:
  import usocket as socket
except:
  import socket

import time, network, gc, ssl, os


gc.collect()


# Load SSL certificates
with open('/certs/server.crt', 'r') as f:
    cert = f.read()

with open('/certs/server.key', 'r') as f:
    key = f.read()


SSID ='OPENMV_AP'   # Network SSID
KEY  ='1234567890'  # Network key (must be 10 chars)
HOST = ''           # Use first available interface
PORT = 8080         # Arbitrary non-privileged port


# Init wlan module in AP mode.
wlan = network.WLAN(network.AP_IF)
wlan.deinit()
wlan.config(essid=SSID, channel=11, password=KEY)
wlan.active(True)

global var1
global var2
var1 = "Variable 1:  12345"
var2 = "Variable 2:  67891"

print("Connect to: ", wlan.ifconfig())

while (len(wlan.status("stations")) == 0):
    print("Waiting for a station to connect...")
    time.sleep_ms(1000)

print("Station connected", ':'.join(["%x"%(x) for x in wlan.status("stations")[0][0]]))

def web_page():
  html = """<html>
  <head><meta name="viewport" content="width=device-width, initial-scale=1"></head>
  <body>
  <h1>Arduino Portenta WebServer!</h1>
  <h2> {var1} </h2>
  <h2> {var2} </h2>
  </body>
  </html>""".format(var1=var1,var2=var2)
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 443))  # Listen on all interfaces, port 443 (HTTPS)
s.listen(5)
# Wrap the socket with SSL
print(key)
print(cert)

s = ssl.wrap_socket(s,server_side=True, key=key, cert=cert, cadata=None, server_hostname=None, do_handshake=True)
print("HTTPS server running on port 443...")

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  print('Content = %s' % str(request))
  response = web_page()
  conn.send(response)
  conn.close()

Here is the full output. I let print the content of the demo certificate files, to check that the cert files are loaded successfully.

OpenMV v4.5.9; MicroPython v1.23.0-r19; Arduino Portenta H7-STM32H747
Type "help()" for more information.
>>> 
raw REPL; CTRL-B to exit
>OKConnect to:  ('192.168.4.1', '255.255.255.0', '192.168.4.1', '0.0.0.0')
Waiting for a station to connect...
Waiting for a station to connect...
Waiting for a station to connect...
Station connected 4:d6:aa:19:36:e
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQWBdP8MbyEfZ4J1Lu
Xa8OuAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIzd1xzYDpFZwEggTI
oOATh5Dwd75BxqeKBeUFaa9Fhw4DmFrpStfj1NHdt356wv6CAHnqu4pvWB+oQ9mr
BGrObCiQ+QvMLBZIG/7PCCGyG512X25AV26TxtAwdlJEpLhnELUtGvClahb6zImF
7QpRF3xJUc6W2BK38qt09kcYS4/Ml2n41nUxRRaFHNq/lwPumyl1Ix29BFUymJyc
GOSHJn/pvLOcm+v1ZDEuU4pi7hpOWuTlUhdLVtwHPqyC8r1AhRFpwEX6NoDCrwA8
22QtoamFHl7qHge3WdY3j1d8qrHxqj3TqfT/ftWRLmV7iiSrxn1XQEh9JWakB2y+
fAeLG31eyx1juHsCm1B6hdZLFoeGbO0YeZ6fm9B49woqWv/5W1ZbwiJpfwb/bL+o
wbiQowGegnKL3JrhoPSqH1OG3aWa+jiIvUkmb4Pud05AnIr+hmBG165a3JOb0A8h
ZsLWi3Zv5OZXin0z8ZbRo+LIPrbkb5w79oYVVWWUtKiHqYkTuX00yk2TLxfxQfY/
MZR/8ftGanIojTLOTWHTgtQ8v3h7UNzq2OCgV8fFuaxmcgz2tsd6C/rwf5JS8PNA
JNZ4YyhjzEKbTeThF1NpNwjI5d6N4/mWpMSLiLfxm7a0y8OlcVVkunHG2+c6GIfq
+UFsdj4Pp8noMwyBWqidqE21W8u2/vgxRtDZNMPvaj8kLHlR5QmmqFpakq3rrZ+4
AHqOqEyD3TSTH3fLRC7it7kydjCgjkmfQK0NWOHRU5wZ7KvYjEqJyAgPeqMkX1dw
zpaanN2f/MwU9/zSq108wie7/mywEGGCfYac9iuP+0EDEUFf2PYjsrHaaq7o2YlP
O4DobvpVonBPMm0qbONYBqQ/hgj3Ja1KUsuU1Ypu6gFSElU1T5w0PzdDV1QE0FN3
FuhhjemcikqeFid6Q5/MBtWc9WcqgBuVDP/TL5RRTwTUNV9+pXd9f3X02O2ckoM1
/9hduddpBRx37Rpbgg0+mNcRAjN38N5eATNex7kqIF2Vf1uFYAuayt+MzadDFZKE
DumP0Sq2i5upwD31FIXDhPX6KJd71E1Q1F/HrO2+QOLbgFi28h0x30Qb9Up6wNol
OTL+QUaMVk9FdcN3DSNSoM2y7vcVwkU8aGTJ4EbD/osBWaTyS8ugCLuo2c+FG770
BfO5TqaSndw4yZ2dUAfA5f7W0XZCp5MWJBoFiKYbW2nqv2x11X77k7yVBHdO7Jok
1Yw7H6f6VDHiUoGN7486McDFbuuhwTxXplFVXZyMd13UeFcDAf05vvlifpDLmKj4
TN3kUzOHLmG09fS+axOyiJU5LqV3yKTnAA6fkQB+eoMJdOPu5TAEV1Lt7Jx5KaAN
knNuGqGeYl0iszkmXB5oamD8j0cDb7leqJphDGIcWHNx3a2f7M7Ij9H3AmF4qqpG
P4epMZgcbx/+So8xeBz1ANMq8r7Ru/ZM3mea5s60YPBq3vevrz9mwL4ItgegahV6
UyqQKFHM6VgYSiC5U0IBsjLXYDG5AYFfM89SFdFdcyd5Nl28WW082aR3UexMoYli
9PiGsb2qypkqI0Cz0xlI33G94DSov6a6ppnOc0rMbkcTIc2RkCchcBNb/iPPfxla
jpTwQwH7IlkWtrpTG6FtKFcBjecSPhYN
-----END ENCRYPTED PRIVATE KEY-----

-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIUaoctpMfmrhuRfmyApGoSlmYJWWEwDQYJKoZIhvcNAQEL
BQAwPjELMAkGA1UEBhMCREUxDDAKBgNVBAgMA0xXTDEhMB8GA1UECgwYSW50ZXJu
ZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTI1MDEyOTE1NTYwNVoXDTI2MDEyOTE1NTYw
NVowPjELMAkGA1UEBhMCREUxDDAKBgNVBAgMA0xXTDEhMB8GA1UECgwYSW50ZXJu
ZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAu/bRz+FiopdDfNMGJbiVqgMK+CvJfYH/rVi7sn408F6EM62bTuWU0djgANTc
wawfoOUgqhTVu77hPICdvwo+h7FMXBpp1I2ID8ak3g66SzJENyNmY7V8hYBPmuIo
x6urS+KHdATj0GCgedA4dxyoiaf/BH53IMNWrx4wYGL20Ip4DoES944VqYrEwMEo
mGYC+znvFmuZ7FFR7CG/g4XtTlBW30w2dVCOatNaj9zXaM41/6sPsYvPgQ9Gvk7I
VkLETDqmHW0gCkKbpTE4ObICqy9vSVqEXDZJepFIyqsKq5PO2OqFAIlo2vD0AJeb
iFMCprmjgrOz1SKPXqoJR/tn7QIDAQABo1MwUTAdBgNVHQ4EFgQU3tlardS3k8Y7
xofOfr51hBEFoOgwHwYDVR0jBBgwFoAU3tlardS3k8Y7xofOfr51hBEFoOgwDwYD
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYaAPZwVyqT7JnVPIRQgp
c/DDvKDvrG8k4iBpGAEW31igYufRc++rgURzsCuec08ReuKn8DLwiOzb6/aiQECz
D1e8BdtNPAFx2mZkvHmKe23DbeE64GpZDU3uZHn8jBwqxOPo7zss0yBDfoi7wDnj
KcErw8rKDE+36aRXv0LlJ/0LMn7sBjis7YWx9wHAzjjr8sfJqHf6qqqFGDD7csj5
4sxxaDgQl8sQTHRIKwE05vbBYtr65D/1OyYL32c/qOFjkvQ9snR53ojTdD9+qBHj
cUMO8CoQcIkKiUTqwP3b1dNsnH8/btTSwvERufmNjWL9Re21kRiPGoXD8l8lb0Q7
5A==
-----END CERTIFICATE-----

Traceback (most recent call last):
  File "<stdin>", line 64, in <module>
  File "ssl.py", line 1, in wrap_socket
  File "ssl.py", line 1, in load_cert_chain
OSError: [Errno 2] ENOENT

For me is not exactly clear, which file could not found or is unable to load.

Thanks.

key and file should be paths to DER encoded files, stored on the filesystem.

Hello,

I created the DER certificates in the following way:

  1. openssl ecparam -name prime256v1 -genkey -noout -outform DER -out key.der
  2. openssl req -new -key key.der -keyform DER -out csr.pem
  3. openssl req -x509 -key key.der -keyform DER -in csr.pem -out cert.der -outform DER -days 365

The files cert.der and key.der have been stored in the local directory:

  • /certs/key.der
  • /certs/cert.der

Then I updated the codes as below:

try:
  import usocket as socket
except:
  import socket

import time, network, gc, ssl, os


gc.collect()


# Load SSL certificates
keyfile = "/certs/key.der"
certfile = "/certs/cert.der"


SSID ='OPENMV_AP'   # Network SSID
KEY  ='1234567890'  # Network key (must be 10 chars)
HOST = ''           # Use first available interface
PORT = 8080         # Arbitrary non-privileged port


# Init wlan module in AP mode.
wlan = network.WLAN(network.AP_IF)
wlan.deinit()
wlan.config(essid=SSID, channel=11, password=KEY)
wlan.active(True)

global var1
global var2
var1 = "Variable 1:  12345"
var2 = "Variable 2:  67891"

print("Connect to: ", wlan.ifconfig())

while (len(wlan.status("stations")) == 0):
    print("Waiting for a station to connect...")
    time.sleep_ms(1000)

print("Station connected", ':'.join(["%x"%(x) for x in wlan.status("stations")[0][0]]))

def web_page():
  html = """<html>
  <head><meta name="viewport" content="width=device-width, initial-scale=1"></head>
  <body>
  <h1>Arduino Portenta WebServer!</h1>
  <h2> {var1} </h2>
  <h2> {var2} </h2>
  </body>
  </html>""".format(var1=var1,var2=var2)
  return html


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(('0.0.0.0', 443))  # Listen on all interfaces, port 443 (HTTPS)
s.listen(5)
# Wrap the socket with SSL

s = ssl.wrap_socket(s,server_side=True, key=keyfile, cert=certfile, cadata=None, server_hostname=None, do_handshake=True)
print("HTTPS server running on port 443...")

while True:
  conn, addr = s.accept()
  print('Got a connection from %s' % str(addr))
  request = conn.recv(1024)
  print('Content = %s' % str(request))
  response = web_page()
  conn.send(response)
  conn.close()

Now I got the following output :

Connect to: (‘192.168.4.1’, ‘255.255.255.0’, ‘192.168.4.1’, ‘0.0.0.0’)
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Waiting for a station to connect…
Station connected 4:d6:aa:19:36:e

After that event, the Arduino reboots and disconnects the USB.
It seems for me that

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

could be problematic…

This is not how server-side sockets are supposed to be created. Please read the docs:

  • server_side selects whether the wrapped socket is on the server or client side. A server-side SSL socket should be created from a normal socket returned from accept() on a non-SSL listening server socket.

https://docs.micropython.org/en/latest/library/ssl.html#ssl.SSLContext.wrap_socket

Meaning, you should accept the socket, and call wrap on the accepted socket. That said it shouldn’t cause a hard-fault, but this is a MicroPython issue, so I’ll report it upstream.

I did get as far as connecting, of course without a key this error is probably expected:

Station connected a0:29:42:56:4f:64
DHCPS: client connected: MAC=a0:29:42:56:4f:64 IP=192.168.4.16
Traceback (most recent call last):
  File "<stdin>", line 51, in <module>
  File "ssl.py", line 1, in wrap_socket
  File "ssl.py", line 1, in wrap_socket
OSError: (-30592, 'MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE')