ssl — SSL/TLS 모듈¶
이 모듈은 클라이언트 측과 서버 측 모두에서 네트워크 소켓에 대한 Transport Layer Security(이전에 널리 알려진 명칭은 “Secure Sockets Layer”) 암호화 및 피어 인증 기능에 대한 접근을 제공합니다.
팁
카메라에서 TLS가 처음이신가요? TLS 인증서 다루기 튜토리얼부터 시작하세요. 이 튜토리얼은 키 유형 선택, 인증서를 카메라가 요구하는 DER 형식으로 생성 및 변환하기, 장치로 가져오기, 서버와 클라이언트 검증하기를 완전한 동작 예제와 함께 단계별로 설명합니다.
참고
MicroPython은 ssl.SSLError를 구현하지 않습니다. SSL/TLS 실패는 대신 OSError로 발생합니다.
예제¶
파일시스템에 저장된 CA 인증서(DER 형식)에 대해 서버의 인증서를 검증하는 TLS 클라이언트:
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")
자신의 인증서와 개인 키(DER 형식)를 제시하는 TLS 서버:
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을 래핑하고 새로운 래핑된 소켓 객체를 반환합니다. 이 함수의 구현은 먼저
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는 피어의 인증서를 검증할 CA 인증서 체인(DER 형식)을 담은 bytes 객체입니다. 현재는 단일 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는 CA 인증서를 담은 bytes 객체입니다. 이 두 인수 중 하나만 제공해야 합니다.
- set_ciphers(ciphers: List[str]) None¶
이 컨텍스트로 생성되는 소켓에 사용할 수 있는 암호를 설정합니다. ciphers는 IANA 암호 스위트 형식의 문자열 리스트여야 합니다.
- 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(일반적으로
SOCK_STREAM유형의 socket.socket 인스턴스)을 받아 기반 스트림을 감싸는 ssl.SSLSocket 인스턴스를 반환합니다. 반환된 객체는read(),write()등과 같은 일반적인 stream 인터페이스 메서드를 갖습니다.server_side는 래핑된 소켓이 서버 측인지 클라이언트 측인지를 선택합니다. 서버 측 SSL 소켓은 비SSL 수신 서버 소켓에서
accept()로 반환된 일반 소켓으로부터 생성되어야 합니다.do_handshake_on_connect는 핸드셰이크를
wrap_socket의 일부로 수행할지, 아니면 초기 읽기 또는 쓰기의 일부로 수행하도록 연기할지를 결정합니다. 블로킹 소켓의 경우 핸드셰이크를 즉시 수행하는 것이 표준입니다. 논블로킹 소켓(즉,wrap_socket에 전달된 sock이 논블로킹 모드인 경우)의 경우 핸드셰이크는 일반적으로 연기되어야 하는데, 그렇지 않으면wrap_socket이 완료될 때까지 블로킹되기 때문입니다.server_hostname은 클라이언트로 사용할 때를 위한 것으로, 수신한 서버 인증서와 대조하여 확인할 호스트 이름을 설정합니다. 또한 Server Name Indication(SNI)을 위한 이름을 설정하여 서버가 적절한 인증서를 제시할 수 있게 합니다.
client_id는 DTLS 서버를 구현할 때만 사용되는 MicroPython 전용 확장 인수입니다. 자세한 내용은 DTLS 지원를 참조하세요.
경고
기본적으로 인증서 검증은 수행되지 않습니다(
ssl.CERT_NONE). 안전한 연결을 위해서는 cert_reqs /SSLContext.verify_mode를ssl.CERT_REQUIRED로 설정하여 피어의 인증서를 검증해야 하며, 그렇지 않으면 연결이 중간자 공격에 취약해집니다.CPython의
wrap_socket은send,recv등 소켓에 일반적인 메서드를 가진SSLSocket객체를 반환합니다. MicroPython의wrap_socket은 이러한 소켓 메서드를 가지지 않는 CPython의SSLObject에 더 유사한 객체를 반환합니다.
- verify_mode¶
피어 인증서 검증 동작을 설정하거나 가져옵니다.
CERT_*상수 중 하나여야 합니다.참고
ssl.CERT_REQUIRED는 장치의 날짜/시간이 올바르게 설정되어 있어야 하며(예: mpremote rtc --set 또는ntptime사용), 클라이언트 측인 경우server_hostname을 지정해야 합니다.
DTLS 지원¶
CPython과의 차이점
이것은 MicroPython 확장입니다.
이 모듈은 SSLContext의 protocol 인수로 사용할 수 있는 PROTOCOL_DTLS_CLIENT 및 PROTOCOL_DTLS_SERVER 상수를 통해 클라이언트 모드와 서버 모드에서 DTLS를 지원합니다.
이 경우 기저 소켓은 데이터그램 소켓처럼 동작할 것으로 기대됩니다(즉, af로 socket.AF_INET을, type으로 socket.SOCK_DGRAM을 사용하여 socket.socket으로 연 소켓처럼).
DTLS 서버 지원¶
MicroPython의 DTLS 서버 지원은 DTLS 1.2에서 요구하는 대로 “Hello Verify”로 구성됩니다. 이는 DTLS 클라이언트에게는 투명하지만, MicroPython에서 DTLS 서버를 구현할 때는 관련된 고려 사항이 있습니다:
서버는
SSLContext.wrap_socket()을 호출할 때 추가 인수 client_id를 전달해야 합니다. 이 ID는 클라이언트를 나타내는 전송 계층별 식별자를 담은bytes객체(또는 유사한 객체)여야 합니다.가장 간단한 방법은
socket.recv_from()에서 반환된(client_ip, client_port)튜플을 바이트 문자열로 변환하는 것입니다. 즉:_, 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호출은 “Hello Verify Required”라는OSError오류와 함께 실패합니다. 이는 DTLS “Hello Verify” 쿠키를 아직 클라이언트가 알지 못하기 때문입니다. 동일한 클라이언트가 두 번째로 연결하면wrap_socket이 성공합니다.“Hello Verify”용 DTLS 쿠키는
SSLContext객체와 연결되어 있으므로, 동일한 클라이언트의 후속 연결을 래핑할 때는 동일한SSLContext객체를 사용해야 합니다. 쿠키 구현에는 타임아웃이 포함되어 있고 연결하는 클라이언트 수에 관계없이 일정한 메모리를 사용하므로, 서버의 수명 동안 동일한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속성에 지원되는 값입니다. 피어로부터 유효한 인증서가 요구됩니다.