ssl — מודול SSL/TLS¶
מודול זה מספק גישה לאמצעי הצפנה ואימות עמיתים של Transport Layer Security (שהיה ידוע בעבר ובהרחבה בשם “Secure Sockets Layer”) עבור network sockets, הן בצד הלקוח והן בצד השרת.
טיפ
חדשים ב-TLS על המצלמה? התחילו עם המדריך עבודה עם אישורי TLS. הוא עובר על בחירת סוגי מפתחות, יצירה והמרה של אישורים לפורמט DER שהמצלמה דורשת, העברתם אל המכשיר, ואימות שרתים ולקוחות – עם דוגמאות עבודה מלאות.
הערה
MicroPython אינו מממש את ssl.SSLError. כשלים של SSL/TLS מועלים כ-OSError במקום.
דוגמאות¶
לקוח TLS, המאמת את האישור של השרת מול אישור CA (בפורמט DER) המאוחסן במערכת הקבצים:
import socket
import ssl
import ntptime
# CERT_REQUIRED checks the certificate's validity dates, so the clock
# must be set (see the certificates tutorial linked above).
ntptime.settime()
# Open a plain TCP connection.
addr = socket.getaddrinfo("example.com", 443)[0][-1]
sock = socket.socket()
sock.connect(addr)
# Wrap it for TLS and require a valid certificate.
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="example.com")
ssock.write(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
print(ssock.read())
ssock.close()
לחיבור מהיר ולא מאובטח (ללא אימות אישור) ניתן להשתמש במקום זאת בפונקציית הנוחות ssl.wrap_socket()
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
שרת TLS, המציג את האישור והמפתח הפרטי שלו (פורמט DER):
import socket
import ssl
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)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key")
while True:
client, addr = sock.accept()
sclient = ctx.wrap_socket(client, server_side=True)
sclient.write(b"hello\n")
sclient.close()
פונקציות¶
- ssl.wrap_socket(sock: Any, server_side: bool = False, key: bytes | None = None, cert: bytes | None = None, cert_reqs: int = CERT_NONE, cadata: bytes | None = None, server_hostname: str | None = None, do_handshake: bool = True) Any¶
עוטף את ה-sock הנתון ומחזיר אובייקט wrapped-socket חדש. מימוש פונקציה זו הוא ליצור תחילה
SSLContextואז לקרוא למתודהSSLContext.wrap_socket()על אובייקט ההקשר הזה. הארגומנטים sock, server_side ו-server_hostname מועברים ללא שינוי לקריאת המתודה. הארגומנט do_handshake מועבר כ-do_handshake_on_connect. לשאר הארגומנטים יש את ההתנהגות הבאה:cert_reqs קובע האם העמית (שרת או לקוח) חייב להציג אישור תקף. שימו לב ש-
ssl.CERT_NONEו-ssl.CERT_OPTIONALאינם מאמתים שום אישור; רקssl.CERT_REQUIREDמאמת.cadata הוא אובייקט bytes המכיל את שרשרת אישורי ה-CA (בפורמט DER) שתאמת את האישור של העמית. כעת נתמך רק אישור יחיד מקודד-DER.
מחלקות¶
- class ssl.SSLContext(protocol: int, /)¶
יוצר מופע SSLContext חדש. הארגומנט protocol חייב להיות אחד מקבועי ה-
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
טוען מפתח פרטי ואת האישור המתאים. ה-certfile הוא מחרוזת עם נתיב הקובץ של האישור. ה-keyfile הוא מחרוזת עם נתיב הקובץ של המפתח הפרטי.
הבדל מ-CPython
הרחבת MicroPython: certfile ו-keyfile יכולים להיות אובייקטי bytes במקום מחרוזות, ובמקרה זה הם מתפרשים כנתוני האישור/המפתח עצמם.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
טוען את שרשרת אישורי ה-CA שתאמת את האישור של העמית. cafile הוא נתיב הקובץ של אישורי ה-CA. cadata הוא אובייקט bytes המכיל את אישורי ה-CA. יש לספק רק אחד מהארגומנטים האלה.
- set_ciphers(ciphers: List[str]) None¶
מגדיר את ה-ciphers הזמינים עבור sockets שנוצרים עם הקשר זה. ciphers צריך להיות רשימה של מחרוזות בפורמט IANA cipher suite .
- wrap_socket(sock: Any, *, server_side: bool = False, do_handshake_on_connect: bool = True, server_hostname: str | None = None, client_id: bytes | None = None) Any¶
מקבל stream sock (בדרך כלל מופע socket.socket מסוג
SOCK_STREAM), ומחזיר מופע של ssl.SSLSocket, העוטף את ה-stream הבסיסי. לאובייקט המוחזר יש את מתודות ממשק ה-stream הרגילות כמוread(),write()וכו«.server_side בוחר האם ה-wrapped socket נמצא בצד השרת או בצד הלקוח. socket SSL בצד השרת צריך להיווצר מ-socket רגיל המוחזר מ-
accept()על socket האזנה שאינו SSL.do_handshake_on_connect קובע האם ה-handshake מתבצע כחלק מ-
wrap_socketאו שהוא נדחה כדי להתבצע כחלק מהקריאות או הכתיבות הראשוניות. עבור blocking sockets ביצוע ה-handshake באופן מיידי הוא הסטנדרט. עבור non-blocking sockets (כלומר כאשר ה-sock שהועבר אלwrap_socketנמצא במצב non-blocking) יש לדחות בדרך כלל את ה-handshake כי אחרתwrap_socketנחסם עד שהוא מסתיים.server_hostname מיועד לשימוש כלקוח, ומגדיר את שם המארח לבדיקה מול אישור השרת שהתקבל. הוא גם מגדיר את השם עבור Server Name Indication (SNI), ומאפשר לשרת להציג את האישור המתאים.
client_id הוא ארגומנט הרחבה ספציפי ל-MicroPython המשמש רק בעת מימוש שרת DTLS. ראו תמיכה ב-DTLS לפרטים.
אזהרה
כברירת מחדל לא מתבצע אימות אישור (
ssl.CERT_NONE). לחיבור מאובטח עליכם לאמת את האישור של העמית על ידי הגדרת cert_reqs /SSLContext.verify_modeל-ssl.CERT_REQUIRED; אחרת החיבור פגיע למתקפות man-in-the-middle.ה-
wrap_socketשל CPython מחזיר אובייקטSSLSocketשיש לו מתודות אופייניות ל-sockets, כגוןsend,recvוכו«. ה-wrap_socketשל MicroPython מחזיר אובייקט דומה יותר ל-SSLObjectשל CPython שאין לו את מתודות ה-socket האלה.
- verify_mode¶
מגדיר או מקבל את ההתנהגות לאימות אישורי עמיתים. חייב להיות אחד מקבועי ה-
CERT_*.הערה
ssl.CERT_REQUIREDדורש שהתאריך/השעה של המכשיר יוגדרו כראוי, למשל באמצעות mpremote rtc --set אוntptime, ויש לציין אתserver_hostnameבצד הלקוח.
תמיכה ב-DTLS¶
הבדל מ-CPython
זוהי הרחבה של MicroPython.
מודול זה תומך ב-DTLS במצב לקוח ובמצב שרת באמצעות הקבועים PROTOCOL_DTLS_CLIENT ו-PROTOCOL_DTLS_SERVER שניתן להשתמש בהם כארגומנט ה-protocol של SSLContext.
במקרה זה ה-socket הבסיסי צפוי להתנהג כ-datagram socket (כלומר כמו ה-socket שנפתח עם socket.socket עם socket.AF_INET כ-af ו-socket.SOCK_DGRAM כ-type).
תמיכה בשרת DTLS¶
תמיכת שרת ה-DTLS של MicroPython מוגדרת עם ”Hello Verify“ כנדרש עבור DTLS 1.2. הדבר שקוף עבור לקוחות DTLS, אך ישנם שיקולים רלוונטיים בעת מימוש שרת DTLS ב-MicroPython:
השרת צריך להעביר ארגומנט נוסף client_id בעת קריאה ל-
SSLContext.wrap_socket(). מזהה זה חייב להיות אובייקטbytes(או דומה) עם מזהה ספציפי-תעבורה המייצג את הלקוח.הגישה הפשוטה ביותר היא להמיר את ה-tuple של
(client_ip, client_port)המוחזר מ-socket.recv_from()למחרוזת בתים, כלומר:_, client_addr = sock.recvfrom(1, socket.MSG_PEEK) sock.connect(client_addr) # Connect back to the client sock = ssl_ctx.wrap_socket(sock, server_side=True, client_id=repr(client_addr).encode())
בפעם הראשונה שלקוח מתחבר, קריאת השרת ל-
wrap_socketתיכשל עם שגיאתOSError”Hello Verify Required“. הסיבה היא שה-cookie של ”Hello Verify“ של DTLS עדיין אינו ידוע ללקוח. אם אותו לקוח מתחבר בפעם שנייה אזwrap_socketיצליח.DTLS cookies עבור ”Hello Verify“ משויכים לאובייקט ה-
SSLContext, ולכן יש להשתמש באותו אובייקטSSLContextכדי לעטוף חיבור עוקב מאותו לקוח. מימוש ה-cookie כולל timeout ובעל שימוש זיכרון קבוע ללא קשר לכמה לקוחות מתחברים, ולכן זה בסדר לעשות שימוש חוזר באותו אובייקטSSLContextלאורך כל חיי השרת.
קבועים¶
- ssl.CERT_NONE: int¶
ערך נתמך עבור הפרמטר cert_reqs, והתכונה
SSLContext.verify_mode. לא מתבצע אימות אישור על העמית.
- ssl.CERT_OPTIONAL: int¶
ערך נתמך עבור הפרמטר cert_reqs, והתכונה
SSLContext.verify_mode. אימות האישור הוא אופציונלי. שימו לב שעל OpenMV Cam זה מתנהג כמוssl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
ערך נתמך עבור הפרמטר cert_reqs, והתכונה
SSLContext.verify_mode. נדרש אישור תקף מהעמית.