Az ebben a részben leírt módszerek és tulajdonságok az ESP8266-ra vonatkoznak. Az Arduino WiFi könyvtár dokumentációja nem foglalkozik velük. Mielőtt teljes körűen dokumentálnák, olvassa el az alábbi információkat.
A BearSSL-könyvtár (az ESP8266-kompatibilitás érdekében végzett módosításokkal és a ROM-táblázatok használatával, amikor csak lehetséges) az összes kriptográfiai és TLS-művelet végrehajtására szolgál. A fő portolt repó elérhető a GitHub-on.
Az SSL-műveletek futtatása jelentős CPU-ciklusokat igényel, ezért ajánlatos minden TLS/SSL-vázlatot 160 MHz-en, és nem az alapértelmezett 80 MHz-en futtatni. Még 160 MHz-en is bizonyos kulcscserék több másodpercig is eltarthatnak. Az ESP8266-ban nincs speciális kriptográfiai hardver, és nincs 32x32=>64-es szorzó, és a program sem a fedélzeti RAM-ban van tárolva, így ennek felgyorsítására keveset lehet tenni.
Tekintse meg a munkamenetekről és a kriptográfiai egyeztetés korlátozásáról szóló részt a gyorsabb módok használatának biztosítására.
A BearSSL nem hajt végre memóriafoglalást futás közben, de szükséges a memória lefoglalása a kapcsolat elején. Két memóriadarabra van szükség: 1. Alkalmazásonkénti másodlagos verem. 2. Csatlakozásonkénti TLS vételi/adási puffer plusz többletterhelés.
Az alkalmazásonkénti másodlagos verem körülbelül 6 KB méretű, és a BearSSL feldolgozás során ideiglenes változókhoz használatos. Csak egy verem szükséges, és azt minden BearSSL::WiFiClientSecure vagy BearSSL::WiFiServerSecure példányosításakor kiosztja. Tehát globális kliens vagy szerver esetén a memória lefoglalásra kerül a setup() meghívása előtt.
A kapcsolatonkénti pufferek körülbelül 22 KB méretűek, de bizonyos körülmények között drasztikusan csökkenthető az MFLN használatával vagy az üzenetméretek korlátozásával. További információért lásd az alábbi MLFN részt.
Számos konfigurációs lehetőség létezik, amelyekhez mutatót kell átadni egy objektumnak (azaz egy privát kulcsra mutató mutatót vagy egy tanúsítványlistát). A memória megőrzése érdekében a BearSSL NEM másolja át az ezeken a mutatókon keresztül bevitt objektumokat, ezért minden BearSSL-be továbbított mutatót meg kell őrizni az ügyfélobjektum élettartama alatt. Például a következő kód hibás:
BearSSL::WiFiClientSecure client;
const char x509CA PROGMEM = ".......";
void setup() {
BearSSL::X509List x509(x509CA);
client.setTrustAnchor(&x509);
}
void loop() {
client.connect("192.168.1.1", 443);
}
Mivel az x509 helyi objektumra mutató mutató a setup() után már nem érvényes, várhatóan összeomlik a fő loop()-ban, ahol az ügyfélobjektum eléri.
Általános szabály, hogy vagy tartsa globálisan az objektumokat, vagy használjon újat a létrehozásukhoz, vagy gondoskodjon arról, hogy minden szükséges objektum ugyanabban a hatókörben éljen, mint az ügyfél.
A következő tárgyalás csak hozzávetőleges képet ad a TLS-ről/HTTPS-ről (ami csak a HTTP TLS-kapcsolaton keresztül) és azokról az összetevőkről, amelyeket az alkalmazásnak kezelnie kell a TLS-kapcsolat létrehozásához. További részletekért tekintse meg a vonatkozó RFC 5246-ot és másokat.
A TLS két szakaszra bontható: a szerver (és potenciálisan kliens) identitásának ellenőrzése, majd az adatblokkok kétirányú titkosítása. A másik partner személyazonosságának ellenőrzése az X509-es tanúsítványokba kódolt kulcsokon keresztül történik, amelyeket opcionálisan egy sor más entitás ír alá.
A BearSSL számos funkciójához kriptográfiai kulcsok szükségesek. Mind a nyilvános, mind a privát kulcsok támogatottak, elliptikus görbe vagy RSA kulcs támogatással.
Nyilvános vagy privát kulcs létrehozásához meglévő PEM-ből (ASCII formátum) vagy DER-ből (bináris formátumból) a legegyszerűbb módszer a konstruktor használata:
BearSSL::PublicKey(const char *pemString)
... or ...
BearSSL::PublicKey(const uint8_t *derArray, size_t derLen)
Vegye figyelembe, hogy ezek a konstruktorok natívan támogatják a PROGMEM karakterláncokat és tömböket, és nincs szükség speciális *_P módokra. Vannak további funkciók a kulcstípus azonosítására és a mögöttes BearSSL védett típusok elérésére, de a felhasználói alkalmazásoknak nincs szükségük rájuk.
A TLS támogatja a munkamenet fogalmát (teljesen független és különbözik a HTTP-munkamenetektől), amely lehetővé teszi az ügyfelek számára, hogy újra csatlakozzanak a kiszolgálóhoz anélkül, hogy újra meg kellene tárgyalniuk a titkosítási beállításokat vagy érvényesíteniük kellene az X509-tanúsítványokat. Ezzel jelentős időt takaríthat meg (3-4 másodperc EC-kulcsok esetén), és energiát takaríthat meg azáltal, hogy az ESP8266-ot hosszú ideig aludni hagyja, újracsatlakoztatja és továbbít néhány mintát az SSL-munkamenet segítségével, majd gyorsabban visszaugrik alvó üzemmódba.
A BearSSL::Session egy átláthatatlan osztály. Alkalmazza a BearSSL::WiFiClientSecure.setSession(&BearSSLSession) metódust az első BearSSL::WiFiClientSecure.connect() előtt, és a kapcsolat működése során frissül a munkamenet paramétereivel. Miután a kapcsolaton meghívták a .close() függvényt, sorosítsa a BearSSL::Session objektumot stabil tárolóra (EEPROM, RTC RAM stb.), és állítsa vissza, mielőtt megpróbálná újracsatlakozni. Lásd a BearSSL_Sessions példát egy részletes példáért.
A Sessions további információkat tartalmaz a sessions API-ról.
Az X509 tanúsítványok a társak azonosítására szolgálnak a TLS-kapcsolatokban. Általában csak a kiszolgáló azonosítja magát, de a kliens is megadhat egy X509 tanúsítványt, ha kívánja (ezt gyakran megteszik az MQTT alkalmazásokban). A tanúsítvány sok mezőt tartalmaz, de alkalmazásainkban a legérdekesebbek a név, a nyilvános kulcs és potenciálisan egy aláírási lánc, amely egy megbízható hatósághoz (például egy globális internetes CA-hoz vagy egy vállalati szintű magán hitelesítésszolgáltatóhoz) vezet vissza.
Minden hívás, amely X509-tanúsítványt vesz igénybe, átveheti az X509-tanúsítványok listáját is, így nincs speciális X509-osztály, egyszerűen BearSSL::X509List (amely csak egyetlen tanúsítványt tartalmazhat).
Tanúsítvány generálása a konstruktor használatával történő érvényesítéshez
BearSSL::X509List(const char *pemX509);
...or...
BearSSL::X509List(const uint8_t *derCert, size_t derLen);
Ha további tanúsítványokat kell hozzáadnia (normál működésben nem valószínű), akkor az ::append() művelet használható.
A dokumentum olvasásához használt webböngésző 100 tanúsító hatóság (CA) listáját vezeti világszerte, amelyekben megbízik, hogy igazolja a webhelyek azonosságát.
Sok esetben az alkalmazás ismeri azt a konkrét CA-t, amelyre szüksége van a web- vagy MQTT-kiszolgálók érvényesítéséhez (gyakran csak egyetlen, önaláíró, privát CA-ja az intézménynek). Egyszerűen töltse be privát CA-ját egy BearSSL::X509List-be, és használja azt bizalmi horgonyként.
Vannak azonban olyan esetek, amikor nem tudja előre, hogy melyik CA-ra lesz szüksége (azaz a felhasználó billentyűzetről lép be egy webhelyre), és a CA-k listáját ugyanúgy meg kell őriznie, mint a webböngészőjét. Ilyen esetekben az alkalmazás összeállítása során létre kell hoznia egy tanúsítványcsomagot a számítógépen, majd az alkalmazás bináris fájljának feltöltésekor fel kell töltenie a certs.ar csomagot LittleFS-re vagy SD-re, majd át kell adnia egy BearSSL::CertStore()-nak a TLS társak érvényesítéshez.
A részletekért lásd a BearSSL_CertStore példát.
Kérjük, látogasson el a BearSSL webhelyére a részletes kriptográfiai információkért. Általában a TLS 1.2, TLS 1.1 és TLS 1.0 támogatja az RSA és az elliptikus görbe kulcsokat, valamint a kivonatoló és szimmetrikus titkosítási kódok nagyon gazdag készletét. Felhívjuk figyelmét, hogy az elliptikus görbe (EC) kulcsműveletei jelentős időt vesznek igénybe.
A BearSSL::WiFiClientSecure az az objektum, amely ténylegesen kezeli a TLS-titkosított WiFi-kapcsolatokat egy távoli szerverhez vagy klienshez. Kibővíti a WiFiClient szolgáltatást, így minimális változtatásokkal használható a nem biztonságos kommunikációt végző kódban.
Mielőtt csatlakozna egy kiszolgálóhoz, a BearSSL::WiFiClientSecure-nek meg kell mondani, hogyan ellenőrizheti a másik gép azonosságát. A BearSSL alapértelmezés szerint nem ellenőrzi a kapcsolatokat, és megtagadja a csatlakozást bármilyen szerverhez.
A BearSSL-nek több mód is megadhatja, hogyan ellenőrizze a távoli kiszolgáló azonosságát. Tekintse meg a BearSSL_Validation példát a következő módszerek valós használatához:
Ne ellenőrizze az X509-tanúsítványokat. Nincs garancia arra, hogy ebben az esetben ez az a szerver, amelyhez Ön szerint csatlakozik.
Tegyük fel, hogy a szerver az adott nyilvános kulcsot használja. Ez nem ellenőrzi a szerver vagy az általa küldött X509 tanúsítvány azonosságát, egyszerűen feltételezi, hogy a nyilvános kulcsa a megadott. Ha a szerver egy későbbi időpontban frissíti a nyilvános kulcsát, a kapcsolatok meghiúsulnak.
Ellenőrzi, hogy a visszaküldött tanúsítvány SHA1 ujjlenyomata megegyezik-e ezzel. Ha a szervertanúsítvány megváltozik, ez meghiúsul. Ha egy 20 bájtos tömböt küldünk be, akkor feltételezzük, hogy ezek a bináris SHA1 értékek. Ha egy char* karakterláncot adunk át, akkor az ember által olvasható hexadecimális értékek sorozataként kerül elemzésre, szóközzel vagy kettősponttal elválasztva (pl. setFingerprint(“00:01:02:03:…:1f”);)
Ezt az ujjlenyomatot a szerver által kiszolgált nyers X509 tanúsítvány számítja ki. Nagyon ritka esetekben ezek a tanúsítványok bizonyos kódolásokkal rendelkeznek, amelyeket az ujjlenyomat vétele előtt normalizálni kell (de a memória megőrzése érdekében a BearSSL nem végzi el ezt a normalizálást, mivel a tanúsítvány teljes példányához RAM kellene), és az kiszámított BearSSL ujjlenyomat nem egyezik az OpenSSL által kiszámított ujjlenyomattal. Ebben az esetben engedélyezheti az SSL hibakeresést, és lekérheti a BearSSL számított ujjlenyomatát, és felhasználhatja azt a kódjában, vagy használhatja a teljes tanúsítvány-érvényesítést. Tekintse meg az eredeti problémát és a hibakeresést itt.
Az átadott tanúsítvány(oka)t megbízható horgonyként használja, és elfogadja az ezek bármelyikével aláírt távoli tanúsítványokat. Ha sok megbízhatósági horgonya van, akkor ésszerű lehet a BearSSL::CertStore használata, mert csak egyetlen megbízható horgonyhoz lesz szüksége RAM-ra (míg a setTrustAnchors hívás memóriát igényel a listában szereplő összes tanúsítványhoz).
A setTrustAnchors és a CertStore esetében az aktuális idő (SNTP-n keresztül beállítva) alapján ellenőrzi a tanúsítványt a listában, ezért az SNTP-nek engedélyezve kell lennie és működnie kell a csatlakozási kísérlet előtt. Ha valamilyen okból nem tudja használni az SNTP-t, manuálisan beállíthatja azt a "jelen időt", amelyet a BearSSL használ a tanúsítvány érvényesítéséhez ezzel a hívással, ahol most a szabványos UNIX idő.
TLS-kiszolgálók kérhetik, hogy az ügyfél azonosítsa magát egy X509-tanúsítvánnyal, amelyet egy általa elismert megbízhatósági horgony (vagyis egy globális TA vagy egy privát CA) írt alá. Ezt általában az MQTT-hez hasonló alkalmazásoknál teszik. Alapértelmezés szerint a kliens nem küld tanúsítványt, és ha tanúsítványra van szükség, a szerver megszakítja a kapcsolatot, és nem lesz lehetséges a kapcsolat.
Beállítja az ügyféltanúsítványt az azt kérő TLS-kiszolgálónak való küldésre. Meg kell hívni a connect() előtt, hogy adjunk hozzá egy tanúsítványt a klienshez arra az esetre, ha a szerver kéri. Vegye figyelembe, hogy a tanúsítványok tanúsítványt és privát kulcsot is tartalmaznak. Mindkettőt a tanúsítványgenerátornak kell biztosítania. Az elliptikus görbe (EC) kulcsokhoz további információkra van szükség, amint az a prototípuson is látható.
Mivel a TLS-t sok megabájt memóriával rendelkező rendszerekre fejlesztették ki, alapértelmezés szerint 16 KB-os pufferre van szükségük a fogadáshoz és az átvitelhez. Ez óriási az ESP8266 esetében, amely mindössze körülbelül 40 KB-os teljes heapot tartalmaz.
Az átviteli puffert kicsivel több mint 512 bájtra minimalizálhatjuk (és meg is tesszük), hogy memóriát takarítsunk meg, mivel a BearSSL képes belsőleg biztosítani, hogy az ennél nagyobb átviteleket kisebb darabokra bontsák, amelyek elférnek. Ez azonban továbbra is meghagyja a 16 KB-os fogadási puffer követelményét, mivel általában nem tudjuk garantálni, hogy a TLS-társ kisebb részeket küldjön.
A TLS 1.2 hozzáadta az MFLN-t, amely lehetővé teszi az ügyfelek számára, hogy kisebb puffereket tárgyaljanak a szerverrel, és csökkentsék az ESP8266 memóriaigényét. Sajnos a BearSSL-nek tudnia kell a pufferméreteket, mielőtt elkezdené a kapcsolatot, ezért az olyan alkalmazásoknak, amelyek kisebb puffereket szeretnének használni, ellenőrizniük kell a távoli kiszolgáló támogatását a connect() előtt.
Használja az egyik ilyen hívást a csatlakozás előtt annak meghatározására, hogy egy adott töredékhossz támogatott-e (a specifikáció szerint a lennek kettős hatványnak kell lennie 512 és 4096 között). Ez nem kezdeményez SSL-kapcsolatot, egyszerűen megnyit egy TCP-portot, és próba kézfogást hajt végre a támogatás ellenőrzésére.
Miután ellenőrizte (vagy előzetesen tudta), hogy az MFLN támogatott, ezzel a hívással beállíthatja a kapcsolati objektum által lefoglalt memóriapufferek méretét. Ezt meg kell hívni a connect() előtt, különben a rendszer figyelmen kívül hagyja.
Bizonyos alkalmazásokban, ahol a TLS-szerver nem támogatja az MFLN-t (ezt az írást nem sokan teszik, mivel ez viszonylag új az OpenSSL-ben), de Ön vezérli mind az ESP8266-ot, mind a szervert, amellyel kommunikál, továbbra is beállíthat kisebb BufferSizes() értéket, ha garantálja, hogy az adattömb nem fogja túlcsordultatni ezeket a puffereket.
Sikeres kapcsolódás után ez a metódus visszaadja, hogy az MFLN egyeztetés sikeres volt-e vagy sem. Ha ez nem sikerült, és csökkentette a fogadási puffert a setBufferSizes értékkel, akkor vételi hibákat tapasztalhat, ha a kiszolgáló a fogadási puffernél nagyobb üzeneteket próbál el küldeni.
Ha egy meghatározott időtartamon belül (általában 30 vagy 60 percen belül, de általában a szerveren konfigurálható) ismételten csatlakozik egy szerverhez, a TLS-munkamenet használható a titkosítási beállítások gyorsítótárazására és a kapcsolatok jelentős felgyorsítására.
A BearSSL számos egyedi és érdekesebb módon meghibásodhat. Használja ezeket a hívásokat, hogy több információhoz jusson, ha valami nem sikerül.
Visszaadja az utolsó BearSSL hibakódot, és opcionálisan beállítja a felhasználó által lefoglalt puffert a hiba ember által olvasható formájára. Ahhoz, hogy csak az utolsó hiba egész kódját kapja meg, csak hívja meg paraméterek nélkül (int errCode = getLastSSLError();).
Nagyon ritkán van ok arra, hogy ezeket a hívásokat használja, de elérhetőek.
Elvesz egy tömböt (a PROGMEM-ben érvényes) vagy egy std::vektort a 16 bites BearSSL titkosítási azonosítókból, és korlátozza a BearSSL-t, hogy csak ezeket használja. Ha a kiszolgálónak más titkosításra van szüksége, a csatlakozás meghiúsul. Ez általában nem hasznos, kivéve azokat az eseteket, amikor egy adott titkosítással szeretne kapcsolódni a szerverekhez. A támogatott titkosításokról a BearSSL fejlécekben talál további információt.
Segítő funkció, amely lényegében a BearSSL-t kevésbé biztonságos titkosításokra korlátozza, mint ahogyan natívan választaná, de ezek hasznosak és gyorsabbak lehetnek, ha a szerver bizonyos titkosítási lehetőségektől függ.
Alapértelmezés szerint a BearSSL TLS 1.0, TLS 1.1 vagy TLS 1.2 protokollokhoz csatlakozik (a távoli oldal kérésétől függően). Ha egy részhalmazra szeretne korlátozni, használja a következő hívást:
A min és max érvényes értékei: BR_TLS10, BR_TLS11, BR_TLS12. Ha csak egyetlen TLS-verziót kíván, a min és a max azonos értékre állítható be.
© Copyright 2017, Ivan Grokhotkov Revision b080c507. Fordította: Maczák András