Przy prostych projektach które będą porozumiewać się z serwerami nad którymi mamy pełną kontrolę można wykorzystać odcisk klucza SSL zapisany bezpośrednio w kodzie aplikacji do weryfikacji serwera. Kiedy chcemy by nasze urządzenie zaczęło komunikować się z innymi sewerami i nie przestało działać kiedy zostanie na nich zmieniony certyfikat konieczne jest skorzystanie z jakieś metody weryfikacji certyfikatów. W tym celu wykorzystamy BearSSL i listę certyfikatów Mozilli.
Skorzystać możemy ze skryptu który utworzy plik certs.ar https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py
#!/usr/bin/python3
# This script pulls the list of Mozilla trusted certificate authorities
# from the web at the "mozurl" below, parses the file to grab the PEM
# for each cert, and then generates DER files in a new ./data directory
# Upload these to a SPIFFS filesystem and use the CertManager to parse
# and use them for your outgoing SSL connections.
#
# Script by Earle F. Philhower, III. Released to the public domain.
from __future__ import print_function
import csv
import os
import sys
from subprocess import Popen, PIPE, call
try:
from urllib.request import urlopen
except:
from urllib2 import urlopen
try:
from StringIO import StringIO
except:
from io import StringIO
# Mozilla's URL for the CSV file with included PEM certs
mozurl = "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV"
# Load the manes[] and pems[] array from the URL
names = []
pems = []
response = urlopen(mozurl)
csvData = response.read()
if sys.version_info[0] > 2:
csvData = csvData.decode('utf-8')
csvFile = StringIO(csvData)
csvReader = csv.reader(csvFile)
for row in csvReader:
names.append(row[0]+":"+row[1]+":"+row[2])
pems.append(row[30])
del names[0] # Remove headers
del pems[0] # Remove headers
# Try and make ./data, skip if present
try:
os.mkdir("data")
except:
pass
derFiles = []
idx = 0
# Process the text PEM using openssl into DER files
for i in range(0, len(pems)):
certName = "data/ca_%03d.der" % (idx);
thisPem = pems[i].replace("'", "")
print(names[i] + " -> " + certName)
ssl = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE)
pipe = ssl.stdin
pipe.write(thisPem.encode('utf-8'))
pipe.close()
ssl.wait()
if os.path.exists(certName):
derFiles.append(certName)
idx = idx + 1
if os.path.exists("data/certs.ar"):
os.unlink("data/certs.ar");
arCmd = ['ar', 'q', 'data/certs.ar'] + derFiles;
call( arCmd )
for der in derFiles:
os.unlink(der)
Skrypt utworzy katalog data i w nim zapisze plik certs.ar
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <CertStoreBearSSL.h>
#include <time.h>
#include <FS.h>
BearSSL::CertStore certStore;
const char* ssid = "SSID";
const char* password = "PASSWD";
void setup() {
SPIFFS.begin();
Serial.begin(115200);
while(!Serial);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi .");
while(WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Connected!");
setClock();
int numCerts = certStore.initCertStore(SPIFFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
Serial.printf("Number of CA certs read: %d\n", numCerts);
if (numCerts == 0) {
Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the SPIFFS directory before running?\n");
return; // Can't connect to anything w/o certs!
}
}
HTTPClient http;
void loop() {
BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure();
// Integrate the cert store with this connection
bear->setCertStore(&certStore);
http.begin(dynamic_cast<WiFiClient&>(*bear), "www.przykladowaStrona.org");
int httpCode = http.GET();
if(httpCode > 0) {
String payload = http.getString();
Serial.println(httpCode);
Serial.println(payload);
}
else {
Serial.println("Cos poszło nie tak jak nalezy...");
}
http.end();
delay(100000000);
}
void setClock() {
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Waiting for NTP time sync: ");
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
ESP korzystający z WiFi, pamięci flash i wykonujący obliczenia może mieć duże chwilowe zapotrzebowanie na prąd. By uniknąć problemów w działaniu może zajść potrzeba by zasilić mikrokontroler z czegoś wydajnieszego prądowo niż programator USB-UART.