Modul ssl --- SSL/TLS

Modul ini menyediakan akses ke fasilitas enkripsi Transport Layer Security (sebelumnya dan secara luas dikenal sebagai "Secure Sockets Layer") serta autentikasi peer untuk soket jaringan, baik sisi klien maupun sisi server.

Tip

Baru mengenal TLS di kamera? Mulailah dengan tutorial Bekerja dengan sertifikat TLS. Tutorial tersebut membahas pemilihan jenis kunci, pembuatan dan konversi sertifikat ke format DER yang diperlukan kamera, cara menempatkannya di perangkat, serta verifikasi server dan klien -- dengan contoh kerja lengkap.

Catatan

MicroPython tidak mengimplementasikan ssl.SSLError. Kegagalan SSL/TLS dimunculkan sebagai OSError.

Contoh

Klien TLS, memverifikasi sertifikat server terhadap sertifikat CA (dalam format DER) yang disimpan di filesystem:

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()

Untuk koneksi cepat yang tidak aman (tanpa validasi sertifikat), fungsi kenyamanan ssl.wrap_socket() dapat digunakan sebagai gantinya:

ssock = ssl.wrap_socket(sock, server_hostname="example.com")

Server TLS, menampilkan sertifikat dan kunci privatnya sendiri (format 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()

Fungsi

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

Membungkus sock yang diberikan dan mengembalikan objek wrapped-socket baru. Implementasi fungsi ini adalah dengan terlebih dahulu membuat SSLContext lalu memanggil metode SSLContext.wrap_socket() pada objek konteks tersebut. Argumen sock, server_side, dan server_hostname diteruskan tanpa perubahan ke pemanggilan metode. Argumen do_handshake diteruskan sebagai do_handshake_on_connect. Argumen-argumen lainnya memiliki perilaku berikut:

  • cert_reqs menentukan apakah peer (server atau klien) harus menunjukkan sertifikat yang valid. Perhatikan bahwa ssl.CERT_NONE dan ssl.CERT_OPTIONAL tidak memvalidasi sertifikat apa pun; hanya ssl.CERT_REQUIRED yang melakukan validasi.

  • cadata adalah objek bytes yang berisi rantai sertifikat CA (dalam format DER) yang akan memvalidasi sertifikat peer. Saat ini hanya satu sertifikat berenkode DER yang didukung.

Kelas

class ssl.SSLContext(protocol: int, /)

Membuat instans SSLContext baru. Argumen protocol harus berupa salah satu konstanta PROTOCOL_*.

load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None

Memuat kunci privat dan sertifikat yang sesuai. certfile adalah string berisi jalur file sertifikat. keyfile adalah string berisi jalur file kunci privat.

Perbedaan dengan CPython

Ekstensi MicroPython: certfile dan keyfile dapat berupa objek bytes alih-alih string, dalam hal ini keduanya diinterpretasikan sebagai data sertifikat/kunci yang sebenarnya.

load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None

Memuat rantai sertifikat CA yang akan memvalidasi sertifikat peer. cafile adalah jalur file sertifikat CA. cadata adalah objek bytes yang berisi sertifikat CA. Hanya salah satu dari argumen-argumen ini yang boleh diberikan.

get_ciphers() List[str]

Mendapatkan daftar cipher yang diaktifkan, dikembalikan sebagai daftar string.

set_ciphers(ciphers: List[str]) None

Mengatur cipher yang tersedia untuk soket yang dibuat dengan konteks ini. ciphers harus berupa daftar string dalam format 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

Mengambil stream sock (biasanya instans socket.socket bertipe SOCK_STREAM), dan mengembalikan instans ssl.SSLSocket yang membungkus stream yang mendasarinya. Objek yang dikembalikan memiliki metode antarmuka stream yang umum seperti read(), write(), dll.

  • server_side memilih apakah soket yang dibungkus berada di sisi server atau klien. Soket SSL sisi server harus dibuat dari soket biasa yang dikembalikan oleh accept() pada soket server listening non-SSL.

  • do_handshake_on_connect menentukan apakah handshake dilakukan sebagai bagian dari wrap_socket atau apakah ditunda untuk dilakukan sebagai bagian dari pembacaan atau penulisan awal. Untuk soket pemblokiran, melakukan handshake segera adalah standar. Untuk soket non-pemblokiran (yaitu ketika sock yang diteruskan ke wrap_socket berada dalam mode non-pemblokiran), handshake umumnya harus ditunda karena jika tidak wrap_socket akan memblokir hingga selesai.

  • server_hostname digunakan sebagai klien, dan mengatur nama host untuk diperiksa terhadap sertifikat server yang diterima. Ini juga mengatur nama untuk Server Name Indication (SNI), memungkinkan server menampilkan sertifikat yang tepat.

  • client_id adalah argumen ekstensi khusus MicroPython yang hanya digunakan saat mengimplementasikan DTLS Server. Lihat Dukungan DTLS untuk detailnya.

Peringatan

Secara default tidak ada validasi sertifikat yang dilakukan (ssl.CERT_NONE). Untuk koneksi yang aman, Anda harus memverifikasi sertifikat peer dengan mengatur cert_reqs / SSLContext.verify_mode ke ssl.CERT_REQUIRED; jika tidak, koneksi rentan terhadap serangan man-in-the-middle.

wrap_socket milik CPython mengembalikan objek SSLSocket yang memiliki metode-metode umum untuk soket, seperti send, recv, dll. wrap_socket milik MicroPython mengembalikan objek yang lebih mirip dengan SSLObject milik CPython yang tidak memiliki metode soket tersebut.

verify_mode

Mengatur atau mendapatkan perilaku untuk verifikasi sertifikat peer. Harus berupa salah satu konstanta CERT_*.

Catatan

ssl.CERT_REQUIRED mengharuskan tanggal/waktu perangkat diatur dengan benar, misalnya menggunakan mpremote rtc --set atau ntptime, dan server_hostname harus ditentukan saat berada di sisi klien.

Dukungan DTLS

Perbedaan dengan CPython

Ini adalah ekstensi MicroPython.

Modul ini mendukung DTLS dalam mode klien dan server melalui konstanta PROTOCOL_DTLS_CLIENT dan PROTOCOL_DTLS_SERVER yang dapat digunakan sebagai argumen protocol dari SSLContext.

Dalam kasus ini, soket yang mendasarinya diharapkan berperilaku sebagai soket datagram (yaitu seperti soket yang dibuka dengan socket.socket dengan socket.AF_INET sebagai af dan socket.SOCK_DGRAM sebagai type).

Dukungan server DTLS

Dukungan server DTLS MicroPython dikonfigurasi dengan "Hello Verify" sebagaimana diperlukan untuk DTLS 1.2. Ini transparan untuk klien DTLS, tetapi ada pertimbangan yang relevan saat mengimplementasikan server DTLS di MicroPython:

  • Server harus meneruskan argumen tambahan client_id saat memanggil SSLContext.wrap_socket(). ID ini harus berupa objek bytes (atau sejenis) dengan pengidentifikasi khusus transport yang mewakili klien.

    Pendekatan paling sederhana adalah mengonversi tuple (client_ip, client_port) yang dikembalikan dari socket.recv_from() menjadi string byte, yaitu:

    _, 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())
    
  • Pertama kali klien terhubung, panggilan server ke wrap_socket akan gagal dengan error OSError "Hello Verify Required". Ini karena cookie DTLS "Hello Verify" belum diketahui oleh klien. Jika klien yang sama terhubung untuk kedua kalinya, maka wrap_socket akan berhasil.

  • Cookie DTLS untuk "Hello Verify" dikaitkan dengan objek SSLContext, sehingga objek SSLContext yang sama harus digunakan untuk membungkus koneksi berikutnya dari klien yang sama. Implementasi cookie mencakup timeout dan memiliki penggunaan memori konstan terlepas dari berapa banyak klien yang terhubung, sehingga tidak masalah menggunakan kembali objek SSLContext yang sama selama masa hidup server.

Konstanta

ssl.PROTOCOL_TLS_CLIENT: int

Nilai yang didukung untuk parameter protocol, memilih mode klien TLS.

ssl.PROTOCOL_TLS_SERVER: int

Nilai yang didukung untuk parameter protocol, memilih mode server TLS.

ssl.PROTOCOL_DTLS_CLIENT: int

Nilai yang didukung untuk parameter protocol, memilih mode klien DTLS.

ssl.PROTOCOL_DTLS_SERVER: int

Nilai yang didukung untuk parameter protocol, memilih mode server DTLS.

ssl.CERT_NONE: int

Nilai yang didukung untuk parameter cert_reqs, dan atribut SSLContext.verify_mode. Tidak ada verifikasi sertifikat yang dilakukan pada peer.

ssl.CERT_OPTIONAL: int

Nilai yang didukung untuk parameter cert_reqs, dan atribut SSLContext.verify_mode. Verifikasi sertifikat bersifat opsional. Perhatikan bahwa pada OpenMV Cam ini berperilaku seperti ssl.CERT_NONE.

ssl.CERT_REQUIRED: int

Nilai yang didukung untuk parameter cert_reqs, dan atribut SSLContext.verify_mode. Sertifikat yang valid diperlukan dari peer.