14.4.3. תעודות חתומות-עצמית¶
תעודה חתומה-עצמית היא הדרך המהירה ביותר לגרום ל-TLS לעבוד בין שני התקנים שבשליטתך: שני הקצוות נותנים אמון בתעודה יחידה שאתה מחולל בעצמך. היא מכסה כל פריסה שבה אתה מגדיר את שני צידי החיבור – רשות אישורים (CA) ציבורית נכנסת לתמונה רק כאשר לקוחות צד-שלישי צריכים להתחבר מבלי שייאמר להם לתת אמון בתעודה מותאמת אישית.
14.4.3.1. יצירת תעודה חתומה-עצמית¶
הרץ OpenSSL על מכונת הפיתוח שלך. ה-subjectAltName (SAN) הוא מה שלקוחות TLS מודרניים בודקים במהלך אימות שם המארח, לכן הגדר אותו לשם(ות) המארח ו/או לכתובת(ות) ה-IP שבהם לקוחות ישתמשו כדי להגיע למצלמה (CN לבדו הוא מורשת ומתעלמים ממנו לקוחות רבים). החלף את DNS:openmv / IP:192.168.1.50 בכתובת שאליה הלקוחות שלך מתחברים בפועל.
ECDSA P-256 – מומלץ:
# Generate a P-256 private key.
openssl ecparam -name prime256v1 -genkey -noout -out server.key
# Self-signed certificate valid for one year, with a SAN.
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
ECDSA P-384 – חזק יותר, גדול/איטי יותר:
openssl ecparam -name secp384r1 -genkey -noout -out server.key
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
RSA-2048 – תאימות מרבית:
openssl req -new -x509 -newkey rsa:2048 -nodes -keyout server.key \
-out server.crt -days 365 -subj "/CN=openmv" \
-addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
הערה
תעודת לקוח (המשמשת לאימות הדדי, למטה) נוצרת בדיוק עם אותן פקודות – אין שום דבר ייחודי-ללקוח בתעודה עצמה. פשוט חולל זוג מפתח/תעודה שני ועצמאי תחת שמות שונים (למשל client.key / client.crt) והשתמש בו על הלקוח כפי שמוצג בדוגמת ה-mTLS. ה-subjectAltName חשוב רק עבור הצד שאת שם המארח שלו העמית מאמת (הלקוח בודק את שם השרת; שום דבר אינו בודק את שם הלקוח), כך שניתן להשמיט אותו עבור תעודת לקוח-בלבד. ה--subj / CN הוא באופן דומה רק תווית על תעודת לקוח – צד השרת כאן בודק רק שהתעודה משתרשרת ל-CA מהימן, הוא לעולם אינו מתאים את השם – לכן הגדר אותו למה שמזהה את אותו לקוח (למשל /CN=sensor-01). שמור על ערך -subj כלשהו בכל מקרה, כך ש-OpenSSL יוכל לחולל את התעודה ללא אינטראקציה.
אורך חיי התעודה נקבע על ידי -days; תעודות פוקעות וחייבות להיות מחוללות ופרוסות מחדש לפני כן.
14.4.3.2. המרה ל-DER¶
המר גם את התעודה וגם את המפתח הפרטי ל-DER לפני העתקתם למצלמה:
openssl x509 -in server.crt -outform DER -out server.der
openssl pkey -in server.key -outform DER -out server.key.der
14.4.3.3. העתקת קבצים למצלמה¶
העתק את קבצי ה-DER למערכת הקבצים של המצלמה – למשל על ידי גרירתם אל כונן ה-USB של ה-OpenMV Cam, או עם mpremote cp server.der : ו-mpremote cp server.key.der :. בצד המאמת, העתק גם את תעודת ה-CA / העמית בפורמט DER.
קבצי ה-DER אינם חייבים להימצא על מערכת הקבצים הניתנת לכתיבה. MicroPython יכול גם לעגן קובץ ROMFS לקריאה-בלבד ב-/rom, ותעודות שמונחות שם נטענות בדיוק כמו כל קובץ אחר – למשל ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). קובץ ROMFS מוכן על מכונת הפיתוח שלך והוא לקריאה-בלבד בזמן ריצה, כך שלא ניתן לשנות את התעודה על ההתקן – שימושי לנעילת יחידת ייצור. שים לב שמפתח פרטי המאוחסן ב-ROMFS עדיין קריא על ידי קוד שרץ על המצלמה; ROMFS מגן מפני שינוי, לא חילוץ. תעודה השוכנת ב-ROMFS ניתנת להחלפה רק על ידי בנייה מחדש וצריבה מחדש של הקובץ.
14.4.3.4. שימוש בתעודה¶
לקוח מלא שמכוון את השעון, פותח שקע, מאמת שרת חתום-עצמית ומחליף נתונים:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
# Open a plain TCP connection.
addr = socket.getaddrinfo("openmv", 8443)[0][-1]
sock = socket.socket()
sock.connect(addr)
# Wrap it for TLS, trusting the server's self-signed certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
ssock = ctx.wrap_socket(sock, server_hostname="openmv")
ssock.write(b"hello\n")
print(ssock.read())
ssock.close()
שרת מלא שמציג את התעודה והמפתח שלו:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(socket.getaddrinfo("0.0.0.0", 8443)[0][-1])
sock.listen(1)
while True:
client, addr = sock.accept()
sclient = ctx.wrap_socket(client, server_side=True)
sclient.write(b"hello\n")
print(sclient.read())
sclient.close()
עבור אימות הדדי (mTLS) השרת דורש ומאמת בנוסף תעודת לקוח, והלקוח מציג תעודה משלו:
# Server side: also demand and verify a client certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="client.der")
# Client side: present a certificate of our own.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_cert_chain("client.der", "client.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
ראה את תיעוד המודול ssl ל-API המלא.
הערה
כל מה שבדף זה חל ללא שינוי על DTLS (TLS על גבי UDP). המפתחות, התעודות, פורמט ה-DER, מודל האמון, שיקולי פקיעת התוקף, וקריאות ה-load_cert_chain / load_verify_locations זהים; רק התעבורה שונה – אתה עוטף שקע socket.SOCK_DGRAM ובוחר ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER במקום קבועי הפרוטוקול TLS. הקמט הנוסף היחיד הוא עוגיית מניעת-זיוף בצד השרת – מצופה שהחיבור הראשון מלקוח חדש ייכשל והלקוח פשוט מנסה שוב; ראה תמיכה ב-DTLS לפרטים.