14.4.5. Julkisen palvelimen varmentaminen (kamera asiakkaana)¶
Kaikki edellisellä sivulla kerrottu siitä, että asiakkaalla on ”jo juurivarmenne”, pätee selaimiin, puhelimiin ja tietokoneisiin – mutta se ei päde kameraan. MicroPythonin ssl ei sisällä mitään valmista luottamusvarastoa: juuri päivitetty kamera ei luota mihinkään varmenneviranomaiseen (CA), ja oletusasetus (ssl.CERT_NONE) ei varmenna mitään ja on täysin altis välimieshyökkäykselle. Niinpä kun kamera on asiakas, joka muodostaa yhteyden ulospäin julkiseen TLS-palvelimeen (HTTPS-API, MQTT-välittäjä, …) ja haluat sen aidosti varmentavan kyseisen palvelimen, sinun on toimitettava luottamusankkuri itse.
Mekaniikka on sama kuin Itse allekirjoitetut varmenteet-sivun itse allekirjoitetun asiakkaan esimerkissä; ainoa ero on, että lataamasi tiedosto on aito CA-varmenne vastapuolen oman varmenteen sijaan:
Hanki CA-varmenne, joka ankkuroi palvelimen ketjun. ”Ankkuroi” tarkoittaa ketjun ylimpänä (tai lähellä huippua) olevaa varmennetta, jonka valitset luottamuksen lähtökohdaksi. TLS-palvelin lähettää lehtivarmenteensa ja yleensä välivarmenteensa; se ei koskaan lähetä juurivarmennettaan. Sinun on hankittava tuo luottamusankkuri itse ja palvelimesta riippumatta – pelkkä luottaminen siihen, mitä palvelin sattuu antamaan, tekisi koko varmentamisen tarkoituksettomaksi.
Selvitä ensin, mikä CA tosiasiassa myönsi palvelimen varmenteen. Esimerkiksi osoitteelle
openmv.ioopenssl s_client -connect openmv.io:443 -showcerts < /dev/nullCertificate chain-lohko luettelee kunkin varmenteen sen kohteen (s:) ja myöntäjän (i:) kanssa; uudempi OpenSSL tulostaa myösa:(avaintyyppi)- jav:(voimassaolo) -rivit, jotka voit jättää tässä huomiotta:Certificate chain 0 s:CN=openmv.io i:C=US, O=Let's Encrypt, CN=E8 1 s:C=US, O=Let's Encrypt, CN=E8 i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
Merkintä 0 on lehtivarmenne (
openmv.io), jonka on myöntänyt välivarmenneE8. Merkintä 1 on tuo välivarmenne, jonka on myöntänyt juurivarmenneISRG Root X1. Ylimmän merkinnän myöntäjä (i:) nimeää juurivarmenteen – tässäISRG Root X1. (Välivarmenne onE8eikä muualla mahdollisesti näkemäsiR10/R11, koskaopenmv.iokäyttää ECDSA-varmennetta; Let’s Encrypt allekirjoittaa ECDSA-lehtivarmenteetE-sarjan välivarmenteillaan ja RSA-lehtivarmenteetR-sarjan välivarmenteillaan. Molemmat ketjuuntuvat juurivarmenteeseenISRG Root X1.)OpenSSL tulostaa myös
depth=-rivejä ja saattaa raportoida juurivarmenteen tilallaVerification: OK. Näin käy vain siksi, että oma tietokoneesi luottaa jo varmenteeseenISRG Root X1– palvelin ei lähettänyt sitä (palvelin ei koskaan lähetä juurivarmennettaan), eikä kamerallakaan, jolla ei ole luottamusvarastoa, ole sitä. Juuri siksi sinun on toimitettava se.Lataa tuo juurivarmenne CA:n omista julkaisemista juurivarmenteista. Let’s Encrypt luetteloi kaikki omansa Let’s Encrypt certificates -sivulla; suora tiedosto ISRG Root X1:lle on isrgrootx1.pem (he tarjoavat sen myös valmiiksi koodattuna muodossa isrgrootx1.der). Muut CA:t julkaisevat omansa vastaavalla ”root certificates” / ”repository” -sivulla; kanoninen julkinen joukko on Mozilla CA -ohjelma (CCADB). Varmista, että haet oikean tiedoston, vertaamalla sen sormenjälkeä CA:n julkaisemaan arvoon (lisää
-inform DER, jos latasit.der-tiedoston):openssl x509 -in isrgrootx1.pem -noout -subject -fingerprint -sha256Jos et halua seurata juurivarmennetta, voit sen sijaan kopioida välivarmenteen suoraan
-showcerts-tulosteesta (toinen-----BEGIN CERTIFICATE------lohko), luottaa siihen ja hyväksyä, että sinun on päivitettävä se aina, kun CA vaihtaa välivarmenteen – huomattavasti useammin kuin juurivarmenteen (katso kompromissit alla).Muunna se DER-muotoon, aivan kuten aiemmin:
openssl x509 -in isrgrootx1.pem -outform DER -out ca.derKopioi
ca.derkameraan (tiedostojärjestelmään tai ROMFS:ään) ja lataa se luottamusankkuriksi:import socket import ssl import ntptime ntptime.settime() # validity check needs the clock addr = socket.getaddrinfo("api.example.com", 443)[0][-1] sock = socket.socket() sock.connect(addr) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.verify_mode = ssl.CERT_REQUIRED ctx.load_verify_locations(cafile="ca.der") ssock = ctx.wrap_socket(sock, server_hostname="api.example.com")
server_hostnamevaaditaan tässä: se ohjaa SNI:tä ja on nimi, joka tarkistetaan palvelimen varmenteensubjectAltName-kentästä.
Vihje
Yleisimmän tapauksen oikotie. Let’s Encrypt on laajimmin käytetty julkinen CA, ja sekä sen RSA- että ECDSA-varmenteet ketjuuntuvat tällä hetkellä juurivarmenteeseen ISRG Root X1 (kuten yllä oleva openmv.io-esimerkki osoittaa). Jos palvelimet, joiden kanssa kamera kommunikoi, käyttävät Let’s Encryptiä, voit ohittaa tarkastelun kokonaan: laita vain isrgrootx1.der kameraan ja kutsu sille load_verify_locations.
Tämä ei saa TLS:ää toimimaan kaikkien sivustojen kanssa. Palvelin, jonka varmenne tulee eri CA:lta (DigiCert, Google Trust Services, Amazon, Sectigo, …), epäonnistuu edelleen varmentamisessa, ja koska kamera luottaa yhteen DER-varmenteeseen ssl.SSLContext -kohtaisesti, et voi niputtaa jokaista juurivarmennetta kuten selain. Epäselvissä tilanteissa tunnista palvelimen todellinen CA yllä kuvatulla tavalla ja luota siihen juurivarmenteeseen.
Se, mihin varmenteeseen luotat, on kompromissi:
Juurivarmenne (suositeltu). Pitkäikäinen – usein vuosikymmeniä – joten
ca.dermuuttuu harvoin. Se edellyttää, että palvelin lähettää välivarmenteensa, jotta mbedTLS voi rakentaa polun lehtivarmenne → välivarmenne → luotettu juurivarmenteesi; käytännössä jokainen oikein määritetty julkinen palvelin tekee näin.Välivarmenne. Toimii myös ja jatkaa toimintaansa, vaikka palvelin jättäisi välivarmenteen pois, mutta välivarmenteet vaihdetaan paljon useammin kuin juurivarmenteet, joten joudut päivittämään
ca.der-tiedoston useammin.Itse lehtivarmenne (varmenteen kiinnitys, pinning). Tiukin, mutta lehtivarmenne muuttuu jokaisessa uusinnassa – noin 90 päivän välein Let’s Encryptillä – joten tällä on järkeä vain silloin, kun hallitset myös palvelinta ja voit työntää uuden kiinnityksen jokaiseen kameraan samanaikaisesti. Tätä juuri itse allekirjoitetun asiakkaan esimerkki tekee.
Muista
ssl.SSLContext.load_verify_locations() ottaa yhden DER-koodatun CA-varmenteen, joten kamera luottaa täsmälleen yhteen ankkuriin kerrallaan. Tavoittaaksesi palvelimia eri CA:iden alaisuudessa käytä erillistä ssl.SSLContext -oliota kullekin ankkurille. Ja koska kyseinen varmenne itsekin lopulta vanhenee tai CA vaihtaa sen, kohtele sitä kuten mitä tahansa muutakin laitteen varmennetta.