14.4.3. 자체 서명 인증서¶
자체 서명 인증서는 사용자가 제어하는 두 장치 사이에서 TLS를 가장 빠르게 동작시키는 방법입니다. 양쪽 모두 사용자가 직접 생성한 단일 인증서를 신뢰합니다. 이는 연결의 양쪽을 모두 직접 구성하는 모든 배포를 포괄합니다 – 공개 인증 기관(Certificate Authority)은 사용자 정의 인증서를 신뢰하라는 지시 없이 제3자 클라이언트가 연결해야 할 때에만 등장합니다.
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 파일을 카메라의 파일 시스템으로 복사하십시오 – 예를 들어 OpenMV Cam의 USB 드라이브에 끌어다 놓거나, 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")
전체 API는 ssl 모듈 문서를 참조하십시오.
참고
이 페이지의 모든 내용은 DTLS (TLS over UDP)에도 그대로 적용됩니다. 키, 인증서, DER 형식, 신뢰 모델, 만료 관련 고려 사항, 그리고 load_cert_chain / load_verify_locations 호출은 동일하며, 전송 방식만 다릅니다. 즉, socket.SOCK_DGRAM 소켓을 래핑하고 TLS 프로토콜 상수 대신 ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER 를 선택합니다. 한 가지 추가로 신경 써야 할 점은 서버 측 스푸핑 방지 쿠키입니다. 새 클라이언트로부터의 첫 연결은 실패하는 것이 정상이며 클라이언트는 단순히 재시도합니다. 자세한 내용은 DTLS 지원 를 참조하세요.