14.4.5. Verifiera en publik server (kameran som klient)¶
Allt på föregående sida om att en klient ”redan har roten” gäller för webbläsare, telefoner och datorer – det gäller inte för kameran. MicroPythons ssl levereras utan något inbyggt förtroendelager: en nyligen flashad kamera litar inte på någon CA alls, och standardvärdet (ssl.CERT_NONE) verifierar ingenting och är vidöppet för en man-in-the-middle. Så när kameran är klienten som ansluter ut till en publik TLS-server (ett HTTPS-API, en MQTT-mäklare, …) och du vill att den verkligen ska verifiera den servern, måste du själv tillhandahålla förtroendeankaret.
Mekaniken är densamma som i exemplet med en självsignerad klient på Självsignerade certifikat; den enda skillnaden är att filen du läser in är ett riktigt CA-certifikat i stället för motpartens eget certifikat:
Skaffa CA-certifikatet som förankrar serverns kedja. ”Förankrar” betyder certifikatet i (eller nära) toppen av serverns kedja som du väljer som din utgångspunkt för förtroende. En TLS-server skickar sitt löv-certifikat och vanligtvis sina mellanliggande certifikat; den skickar aldrig sin rot. Du måste själv skaffa det förtroendeankaret och oberoende av servern – att helt enkelt lita på vad än en server ger dig skulle omintetgöra hela poängen med verifiering.
Ta först reda på vilken CA som faktiskt utfärdade serverns certifikat. Till exempel mot
openmv.ioopenssl s_client -connect openmv.io:443 -showcerts < /dev/nullBlocket
Certificate chainlistar varje certifikat med dess subjekt (s:) och utfärdare (i:); nyare OpenSSL skriver även ut rader meda:(nyckeltyp) ochv:(giltighet) som du kan ignorera här: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
Post 0 är löv-certifikatet (
openmv.io), utfärdat av det mellanliggandeE8. Post 1 är det mellanliggande certifikatet, utfärdat av rotenISRG Root X1. Utfärdaren (i:) för den översta posten namnger roten – härISRG Root X1. (Det mellanliggande certifikatet ärE8snarare än detR10/R11du kan ha sett på andra ställen eftersomopenmv.ioanvänder ett ECDSA-certifikat; Let’s Encrypt signerar ECDSA-löv med sina mellanliggande certifikat iE-serien och RSA-löv med sina iR-serien. Båda kedjar tillISRG Root X1.)OpenSSL skriver också ut rader med
depth=och kan rapportera roten medVerification: OK. Det sker bara därför att din dator redan litar påISRG Root X1– servern skickade den inte (en server skickar aldrig sin rot), och kameran, som saknar förtroendelager, kommer inte heller att ha den. Det är precis därför du måste tillhandahålla den.Ladda ner den roten från CA:ns egna publicerade rötter. Let’s Encrypt katalogiserar alla sina på Let’s Encrypts certifikatsida; den direkta filen för ISRG Root X1 är isrgrootx1.pem (de erbjuder den även förkodad som isrgrootx1.der). Andra CA:er publicerar sina på en liknande sida för ”rotcertifikat” / ”repository”; den kanoniska publika uppsättningen är Mozillas CA-program (CCADB). Bekräfta att du hämtade rätt fil genom att jämföra dess fingeravtryck mot värdet som CA:n publicerar (lägg till
-inform DERom du laddade ner.der):openssl x509 -in isrgrootx1.pem -noout -subject -fingerprint -sha256Om du hellre slipper hålla reda på en rot kan du i stället kopiera det mellanliggande certifikatet direkt från
-showcerts-utdata (det andra blocket-----BEGIN CERTIFICATE-----), lita på det, och acceptera att du måste uppdatera det varje gång CA:n roterar det mellanliggande certifikatet – långt oftare än roten (se avvägningen nedan).Konvertera det till DER, precis som tidigare:
openssl x509 -in isrgrootx1.pem -outform DER -out ca.derKopiera
ca.dertill kameran (filsystem eller ROMFS) och läs in det som förtroendeankare: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_hostnamekrävs här: det driver SNI och är namnet som kontrolleras mot serverns certifikatssubjectAltName.
Tips
Genväg för det vanligaste fallet. Let’s Encrypt är den mest använda publika CA:n, och både dess RSA- och ECDSA-certifikat kedjar för närvarande till ISRG Root X1 (som exemplet med openmv.io ovan visar). Om servrarna din kamera pratar med använder Let’s Encrypt kan du hoppa över inspektionen helt: lägg bara isrgrootx1.der på kameran och kör load_verify_locations på den.
Detta gör inte att TLS fungerar mot varje webbplats. En server vars certifikat kommer från en annan CA (DigiCert, Google Trust Services, Amazon, Sectigo, …) kommer fortfarande att misslyckas med verifieringen, och eftersom kameran litar på ett enda DER-certifikat per ssl.SSLContext kan du inte bunta ihop varje rot på det sätt en webbläsare gör. Vid tveksamhet, identifiera serverns faktiska CA som visas ovan och lita på den roten.
Vilket certifikat du litar på är en avvägning:
Roten (rekommenderas). Långlivad – ofta i decennier – så
ca.derändras sällan. Den kräver att servern skickar sitt mellanliggande certifikat så att mbedTLS kan bygga sökvägen löv → mellanliggande → din betrodda rot; praktiskt taget varje korrekt konfigurerad publik server gör det.Det mellanliggande certifikatet. Fungerar också, och fortsätter fungera även om en server utelämnar det mellanliggande certifikatet, men mellanliggande certifikat roteras långt oftare än rötter, så du måste uppdatera
ca.deroftare.Själva löv-certifikatet (certifikatpinning). Snävast, men löv-certifikatet ändras vid varje förnyelse – ungefär var 90:e dag för Let’s Encrypt – så detta är bara meningsfullt när du också kontrollerar servern och kan distribuera den nya pinningen till varje kamera samtidigt. Det är precis vad exemplet med en självsignerad klient gör.
Anteckning
ssl.SSLContext.load_verify_locations() tar ett enda DER-kodat CA-certifikat, så kameran litar på exakt ett ankare åt gången. För att nå servrar under olika CA:er, använd en separat ssl.SSLContext per ankare. Och eftersom det certifikatet i sin tur så småningom kommer att löpa ut eller roteras av CA:n, behandla det som vilket annat certifikat som helst på enheten.