mirror of
https://github.com/Links2004/arduinoWebSockets.git
synced 2025-07-12 15:06:30 +02:00
Add support for CA bundles (#885)
Why: - Allow CA cert bundles to be used This change addresses the need by: - Adding a constructor that takes a pointer to the bundle - Setting the WiFiClientSecure to use the bundle - Adding an example
This commit is contained in:
10
README.md
10
README.md
@ -48,6 +48,16 @@ a WebSocket Server and Client for Arduino based on RFC6455.
|
||||
by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
|
||||
sample Nginx server configuration file to enable this.
|
||||
|
||||
### Root CA Cert Bundles for SSL/TLS connections ###
|
||||
|
||||
Secure connections require the certificate of the server to be verified. One option is to provide a single certificate in the chain of trust. However, for flexibility and robustness, a certificate bundle is recommended. If a server changes the root CA from which it derives its certificates, this will not be a problem. With a single CA cert it will not connect.
|
||||
|
||||
- For [technical details](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/protocols/esp_crt_bundle.html)
|
||||
- For a [PlatformIO setup](https://github.com/Duckle29/esp32-certBundle/)
|
||||
- For an [example](examples/esp32/WebSocketClientSSLBundle/)
|
||||
|
||||
Including a bundle with all CA certs will use 77.2 kB but this list can be reduced to 16.5 kB for the 41 most common. This results in 90% absolute usage coverage and 99% market share coverage according to [W3Techs](https://w3techs.com/technologies/overview/ssl_certificate). The bundle is inserted into the compiled firmware. The bundle is not loaded into RAM, only its index.
|
||||
|
||||
### ESP Async TCP ###
|
||||
|
||||
This libary can run in Async TCP mode on the ESP.
|
||||
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* main.cpp
|
||||
*
|
||||
* Created on: 15.06.2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
// Use the incbin library to embedd the cert binary
|
||||
// extern const uint8_t rootca_crt_bundle_start[] asm(
|
||||
// "_binary_data_cert_x509_crt_bundle_bin_start");
|
||||
|
||||
WiFiMulti wifiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
USE_SERIAL.print(F("Waiting for NTP time sync: "));
|
||||
time_t nowSecs = time(nullptr);
|
||||
while(nowSecs < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
USE_SERIAL.print(F("."));
|
||||
yield();
|
||||
nowSecs = time(nullptr);
|
||||
}
|
||||
|
||||
USE_SERIAL.println();
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&nowSecs, &timeinfo);
|
||||
USE_SERIAL.print(F("Current time: "));
|
||||
USE_SERIAL.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
void hexdump(const void * mem, uint32_t len, uint8_t cols = 16) {
|
||||
const uint8_t * src = (const uint8_t *)mem;
|
||||
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
}
|
||||
USE_SERIAL.printf("%02X ", *src);
|
||||
src++;
|
||||
}
|
||||
USE_SERIAL.printf("\n");
|
||||
}
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
USE_SERIAL.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
// send message to server when Connected
|
||||
webSocket.sendTXT("Connected");
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
|
||||
// send data to server
|
||||
// webSocket.sendBIN(payload, length);
|
||||
break;
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
wifiMulti.addAP("SSID", "WIFI_PASSPHRASE");
|
||||
|
||||
// WiFi.disconnect();
|
||||
while(wifiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
setClock();
|
||||
|
||||
// server address, port and URL. This server can be flakey.
|
||||
// Expected response: Request served by 0123456789abcdef
|
||||
// webSocket.beginSslWithBundle("echo.websocket.org", 443, "/", rootca_crt_bundle_start, "");
|
||||
webSocket.beginSslWithBundle("echo.websocket.org", 443, "/", NULL, "");
|
||||
|
||||
// event handler
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
// use HTTP Basic Authorization this is optional enable if needed
|
||||
// webSocket.setAuthorization("user", "Password");
|
||||
|
||||
// try ever 5000 again if connection has failed
|
||||
webSocket.setReconnectInterval(5000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
8
examples/esp32_pio/WebSocketClientSSLBundle/.gitignore
vendored
Normal file
8
examples/esp32_pio/WebSocketClientSSLBundle/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
*secret*
|
||||
!*secrets.hpp.template
|
||||
*x509_crt_bundle.bin
|
3581
examples/esp32_pio/WebSocketClientSSLBundle/cacrt_all.pem
Normal file
3581
examples/esp32_pio/WebSocketClientSSLBundle/cacrt_all.pem
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@
|
||||
Owner,Common Name or Certificate Name
|
||||
Amazon Trust Services,Amazon Root CA 1
|
||||
Amazon Trust Services,Amazon Root CA 2
|
||||
Amazon Trust Services,Amazon Root CA 3
|
||||
Amazon Trust Services,Amazon Root CA 4
|
||||
Amazon Trust Services,Starfield Services Root Certificate Authority - G2
|
||||
DigiCert,Baltimore CyberTrust Root
|
||||
DigiCert,Cybertrust Global Root
|
||||
DigiCert,DigiCert Assured ID Root CA
|
||||
DigiCert,DigiCert Assured ID Root G2
|
||||
DigiCert,DigiCert Assured ID Root G3
|
||||
DigiCert,DigiCert Global Root CA
|
||||
DigiCert,DigiCert Global Root G2
|
||||
DigiCert,DigiCert Global Root G3
|
||||
DigiCert,DigiCert High Assurance EV Root CA
|
||||
DigiCert,DigiCert Trusted Root G4
|
||||
GlobalSign,GlobalSign ECC Root CA - R5
|
||||
GlobalSign,GlobalSign Root CA - R3
|
||||
GlobalSign,GlobalSign Root CA - R6
|
||||
GlobalSign,GlobalSign Root CA
|
||||
GoDaddy,Go Daddy Class 2 CA
|
||||
GoDaddy,Go Daddy Root Certificate Authority - G2
|
||||
GoDaddy,Starfield Class 2 CA
|
||||
GoDaddy,Starfield Root Certificate Authority - G2
|
||||
Google Trust Services LLC (GTS),GlobalSign ECC Root CA - R4
|
||||
Google Trust Services LLC (GTS),GlobalSign Root CA - R2
|
||||
Google Trust Services LLC (GTS),GTS Root R1
|
||||
Google Trust Services LLC (GTS),GTS Root R2
|
||||
Google Trust Services LLC (GTS),GTS Root R3
|
||||
Google Trust Services LLC (GTS),GTS Root R4
|
||||
"IdenTrust Services, LLC",DST Root CA X3
|
||||
"IdenTrust Services, LLC",IdenTrust Commercial Root CA 1
|
||||
"IdenTrust Services, LLC",IdenTrust Public Sector Root CA 1
|
||||
Sectigo,Comodo AAA Services root
|
||||
Sectigo,COMODO Certification Authority
|
||||
Sectigo,COMODO ECC Certification Authority
|
||||
Sectigo,COMODO RSA Certification Authority
|
||||
Sectigo,USERTrust ECC Certification Authority
|
||||
Sectigo,USERTrust RSA Certification Authority
|
|
227
examples/esp32_pio/WebSocketClientSSLBundle/gen_crt_bundle.py
Normal file
227
examples/esp32_pio/WebSocketClientSSLBundle/gen_crt_bundle.py
Normal file
@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# ESP32 x509 certificate bundle generation utility
|
||||
#
|
||||
# Converts PEM and DER certificates to a custom bundle format which stores just the
|
||||
# subject name and public key to reduce space
|
||||
#
|
||||
# The bundle will have the format: number of certificates; crt 1 subject name length; crt 1 public key length;
|
||||
# crt 1 subject name; crt 1 public key; crt 2...
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http:#www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
from io import open
|
||||
|
||||
try:
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
except ImportError:
|
||||
print('The cryptography package is not installed.'
|
||||
'Please refer to the Get Started section of the ESP-IDF Programming Guide for '
|
||||
'setting up the required packages.')
|
||||
raise
|
||||
|
||||
ca_bundle_bin_file = 'x509_crt_bundle'
|
||||
|
||||
quiet = False
|
||||
|
||||
|
||||
def status(msg):
|
||||
""" Print status message to stderr """
|
||||
if not quiet:
|
||||
critical(msg)
|
||||
|
||||
|
||||
def critical(msg):
|
||||
""" Print critical message to stderr """
|
||||
sys.stderr.write('gen_crt_bundle.py: ')
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
class CertificateBundle:
|
||||
def __init__(self):
|
||||
self.certificates = []
|
||||
self.compressed_crts = []
|
||||
|
||||
if os.path.isfile(ca_bundle_bin_file):
|
||||
os.remove(ca_bundle_bin_file)
|
||||
|
||||
def add_from_path(self, crts_path):
|
||||
|
||||
found = False
|
||||
for file_path in os.listdir(crts_path):
|
||||
found |= self.add_from_file(os.path.join(crts_path, file_path))
|
||||
|
||||
if found is False:
|
||||
raise InputError('No valid x509 certificates found in %s' % crts_path)
|
||||
|
||||
def add_from_file(self, file_path):
|
||||
try:
|
||||
if file_path.endswith('.pem'):
|
||||
status('Parsing certificates from %s' % file_path)
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_pem(crt_str)
|
||||
return True
|
||||
|
||||
elif file_path.endswith('.der'):
|
||||
status('Parsing certificates from %s' % file_path)
|
||||
with open(file_path, 'rb') as f:
|
||||
crt_str = f.read()
|
||||
self.add_from_der(crt_str)
|
||||
return True
|
||||
|
||||
except ValueError:
|
||||
critical('Invalid certificate in %s' % file_path)
|
||||
raise InputError('Invalid certificate')
|
||||
|
||||
return False
|
||||
|
||||
def add_from_pem(self, crt_str):
|
||||
""" A single PEM file may have multiple certificates """
|
||||
|
||||
crt = ''
|
||||
count = 0
|
||||
start = False
|
||||
|
||||
for strg in crt_str.splitlines(True):
|
||||
if strg == '-----BEGIN CERTIFICATE-----\n' and start is False:
|
||||
crt = ''
|
||||
start = True
|
||||
elif strg == '-----END CERTIFICATE-----\n' and start is True:
|
||||
crt += strg + '\n'
|
||||
start = False
|
||||
self.certificates.append(x509.load_pem_x509_certificate(crt.encode(), default_backend()))
|
||||
count += 1
|
||||
if start is True:
|
||||
crt += strg
|
||||
|
||||
if(count == 0):
|
||||
raise InputError('No certificate found')
|
||||
|
||||
status('Successfully added %d certificates' % count)
|
||||
|
||||
def add_from_der(self, crt_str):
|
||||
self.certificates.append(x509.load_der_x509_certificate(crt_str, default_backend()))
|
||||
status('Successfully added 1 certificate')
|
||||
|
||||
def create_bundle(self):
|
||||
# Sort certificates in order to do binary search when looking up certificates
|
||||
self.certificates = sorted(self.certificates, key=lambda cert: cert.subject.public_bytes(default_backend()))
|
||||
|
||||
bundle = struct.pack('>H', len(self.certificates))
|
||||
|
||||
for crt in self.certificates:
|
||||
""" Read the public key as DER format """
|
||||
pub_key = crt.public_key()
|
||||
pub_key_der = pub_key.public_bytes(serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo)
|
||||
|
||||
""" Read the subject name as DER format """
|
||||
sub_name_der = crt.subject.public_bytes(default_backend())
|
||||
|
||||
name_len = len(sub_name_der)
|
||||
key_len = len(pub_key_der)
|
||||
len_data = struct.pack('>HH', name_len, key_len)
|
||||
|
||||
bundle += len_data
|
||||
bundle += sub_name_der
|
||||
bundle += pub_key_der
|
||||
|
||||
return bundle
|
||||
|
||||
def add_with_filter(self, crts_path, filter_path):
|
||||
|
||||
filter_set = set()
|
||||
with open(filter_path, 'r', encoding='utf-8') as f:
|
||||
csv_reader = csv.reader(f, delimiter=',')
|
||||
|
||||
# Skip header
|
||||
next(csv_reader)
|
||||
for row in csv_reader:
|
||||
filter_set.add(row[1])
|
||||
|
||||
status('Parsing certificates from %s' % crts_path)
|
||||
crt_str = []
|
||||
with open(crts_path, 'r', encoding='utf-8') as f:
|
||||
crt_str = f.read()
|
||||
|
||||
# Split all certs into a list of (name, certificate string) tuples
|
||||
pem_crts = re.findall(r'(^.+?)\n(=+\n[\s\S]+?END CERTIFICATE-----\n)', crt_str, re.MULTILINE)
|
||||
|
||||
filtered_crts = ''
|
||||
for name, crt in pem_crts:
|
||||
if name in filter_set:
|
||||
filtered_crts += crt
|
||||
|
||||
self.add_from_pem(filtered_crts)
|
||||
|
||||
|
||||
class InputError(RuntimeError):
|
||||
def __init__(self, e):
|
||||
super(InputError, self).__init__(e)
|
||||
|
||||
|
||||
def main():
|
||||
global quiet
|
||||
|
||||
parser = argparse.ArgumentParser(description='ESP-IDF x509 certificate bundle utility')
|
||||
|
||||
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
|
||||
parser.add_argument('--input', '-i', nargs='+', required=True,
|
||||
help='Paths to the custom certificate folders or files to parse, parses all .pem or .der files')
|
||||
parser.add_argument('--filter', '-f', help='Path to CSV-file where the second columns contains the name of the certificates \
|
||||
that should be included from cacrt_all.pem')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
quiet = args.quiet
|
||||
|
||||
bundle = CertificateBundle()
|
||||
|
||||
for path in args.input:
|
||||
if os.path.isfile(path):
|
||||
if os.path.basename(path) == 'cacrt_all.pem' and args.filter:
|
||||
bundle.add_with_filter(path, args.filter)
|
||||
else:
|
||||
bundle.add_from_file(path)
|
||||
elif os.path.isdir(path):
|
||||
bundle.add_from_path(path)
|
||||
else:
|
||||
raise InputError('Invalid --input=%s, is neither file nor folder' % args.input)
|
||||
|
||||
status('Successfully added %d certificates in total' % len(bundle.certificates))
|
||||
|
||||
crt_bundle = bundle.create_bundle()
|
||||
|
||||
with open(ca_bundle_bin_file, 'wb') as f:
|
||||
f.write(crt_bundle)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except InputError as e:
|
||||
print(e)
|
||||
sys.exit(2)
|
46
examples/esp32_pio/WebSocketClientSSLBundle/lib/README
Normal file
46
examples/esp32_pio/WebSocketClientSSLBundle/lib/README
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
25
examples/esp32_pio/WebSocketClientSSLBundle/platformio.ini
Normal file
25
examples/esp32_pio/WebSocketClientSSLBundle/platformio.ini
Normal file
@ -0,0 +1,25 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
upload_speed = 921600
|
||||
build_flags =
|
||||
-DCORE_DEBUG_LEVEL=5
|
||||
lib_deps = ../../../src
|
||||
|
||||
extra_scripts =
|
||||
pre:run_gen_script.py
|
||||
|
||||
board_build.embed_txtfiles =
|
||||
data/cert/x509_crt_bundle.bin
|
12
examples/esp32_pio/WebSocketClientSSLBundle/readme.md
Normal file
12
examples/esp32_pio/WebSocketClientSSLBundle/readme.md
Normal file
@ -0,0 +1,12 @@
|
||||
This is a PlatformIO project that uses a modified WiFiClientSecure library (in `lib`) to
|
||||
implement proper SSL support using root certificates as discussed
|
||||
[here](https://github.com/espressif/arduino-esp32/issues/3646#issuecomment-648292677)
|
||||
|
||||
It is based on the work by [meltdonw03](https://github.com/meltdown03) in that thread, and the
|
||||
[BasicHttpsClient example](https://github.com/espressif/arduino-esp32/blob/1.0.4/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino) from the arduino-esp32 project.
|
||||
|
||||
Just copy `include/secrets.hpp.template` to `include/secrets.hpp` and fill in your WiFi details.
|
||||
Then it should be pretty much ready to go. The local WiFiClientSecure library should take priority.
|
||||
Debug is set to verbose, so you'll see a lot of noise, but there should also be this readme on success :)
|
||||
|
||||
To get a current CA cert bundle download it from [curl's website](https://curl.se/docs/caextract.html).
|
@ -0,0 +1,6 @@
|
||||
Import("env")
|
||||
|
||||
env.Execute("$PYTHONEXE -m pip install cryptography")
|
||||
env.Execute("$PYTHONEXE gen_crt_bundle.py --input cacrt_all.pem")
|
||||
env.Execute("mkdir -p data/cert")
|
||||
env.Execute("mv -f x509_crt_bundle data/cert/x509_crt_bundle.bin")
|
127
examples/esp32_pio/WebSocketClientSSLBundle/src/main.cpp
Normal file
127
examples/esp32_pio/WebSocketClientSSLBundle/src/main.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* main.cpp
|
||||
*
|
||||
* Created on: 15.06.2024
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
extern const uint8_t rootca_crt_bundle_start[] asm(
|
||||
"_binary_data_cert_x509_crt_bundle_bin_start");
|
||||
|
||||
WiFiMulti wifiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
void setClock() {
|
||||
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||
|
||||
USE_SERIAL.print(F("Waiting for NTP time sync: "));
|
||||
time_t nowSecs = time(nullptr);
|
||||
while(nowSecs < 8 * 3600 * 2) {
|
||||
delay(500);
|
||||
USE_SERIAL.print(F("."));
|
||||
yield();
|
||||
nowSecs = time(nullptr);
|
||||
}
|
||||
|
||||
USE_SERIAL.println();
|
||||
struct tm timeinfo;
|
||||
gmtime_r(&nowSecs, &timeinfo);
|
||||
USE_SERIAL.print(F("Current time: "));
|
||||
USE_SERIAL.print(asctime(&timeinfo));
|
||||
}
|
||||
|
||||
void hexdump(const void * mem, uint32_t len, uint8_t cols = 16) {
|
||||
const uint8_t * src = (const uint8_t *)mem;
|
||||
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(i % cols == 0) {
|
||||
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
|
||||
}
|
||||
USE_SERIAL.printf("%02X ", *src);
|
||||
src++;
|
||||
}
|
||||
USE_SERIAL.printf("\n");
|
||||
}
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
USE_SERIAL.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED:
|
||||
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
// send message to server when Connected
|
||||
webSocket.sendTXT("Connected");
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
|
||||
hexdump(payload, length);
|
||||
|
||||
// send data to server
|
||||
// webSocket.sendBIN(payload, length);
|
||||
break;
|
||||
case WStype_ERROR:
|
||||
case WStype_FRAGMENT_TEXT_START:
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
wifiMulti.addAP("SSID", "WIFI_PASSPHRASE");
|
||||
|
||||
// WiFi.disconnect();
|
||||
while(wifiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
setClock();
|
||||
|
||||
// server address, port and URL. This server can be flakey.
|
||||
// Expected response: Request served by 0123456789abcdef
|
||||
webSocket.beginSslWithBundle("echo.websocket.org", 443, "/", rootca_crt_bundle_start, "");
|
||||
|
||||
// event handler
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
// use HTTP Basic Authorization this is optional enable if needed
|
||||
// webSocket.setAuthorization("user", "Password");
|
||||
|
||||
// try ever 5000 again if connection has failed
|
||||
webSocket.setReconnectInterval(5000);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
@ -48,6 +48,9 @@ void WebSocketsClient::begin(const char * host, uint16_t port, const char * url,
|
||||
#if defined(HAS_SSL)
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = NULL;
|
||||
#ifdef ESP32
|
||||
_CA_bundle = NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
_client.num = 0;
|
||||
@ -107,6 +110,7 @@ void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * u
|
||||
_client.isSSL = true;
|
||||
_fingerprint = fingerprint;
|
||||
_CA_cert = NULL;
|
||||
_CA_bundle = NULL;
|
||||
}
|
||||
|
||||
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
|
||||
@ -118,7 +122,16 @@ void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const ch
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = CA_cert;
|
||||
_CA_bundle = NULL;
|
||||
}
|
||||
void WebSocketsClient::beginSslWithBundle(const char * host, uint16_t port, const char * url, const uint8_t * CA_bundle, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
_client.isSSL = true;
|
||||
_fingerprint = SSL_FINGERPRINT_NULL;
|
||||
_CA_cert = NULL;
|
||||
_CA_bundle = CA_bundle;
|
||||
}
|
||||
|
||||
#else
|
||||
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const uint8_t * fingerprint, const char * protocol) {
|
||||
begin(host, port, url, protocol);
|
||||
@ -231,6 +244,11 @@ void WebSocketsClient::loop(void) {
|
||||
#else
|
||||
#error setCACert not implemented
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
} else if(_CA_bundle) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] setting CA bundle");
|
||||
_client.ssl->setCACertBundle(_CA_bundle);
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
} else if(!SSL_FINGERPRINT_IS_SET) {
|
||||
_client.ssl->setInsecure();
|
||||
|
@ -53,6 +53,9 @@ class WebSocketsClient : protected WebSockets {
|
||||
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
|
||||
#endif
|
||||
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
|
||||
#ifdef ESP32
|
||||
void beginSslWithBundle(const char * host, uint16_t port, const char * url = "/", const uint8_t * CA_bundle = NULL, const char * protocol = "arduino");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void beginSocketIO(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
|
||||
@ -112,6 +115,7 @@ class WebSocketsClient : protected WebSockets {
|
||||
#ifdef SSL_AXTLS
|
||||
String _fingerprint;
|
||||
const char * _CA_cert;
|
||||
const uint8_t * _CA_bundle;
|
||||
#define SSL_FINGERPRINT_IS_SET (_fingerprint.length())
|
||||
#define SSL_FINGERPRINT_NULL ""
|
||||
#else
|
||||
|
Reference in New Issue
Block a user