14.4.3. ใบรับรอง Self-signed

ใบรับรอง self-signed เป็นวิธีที่เร็วที่สุดในการให้ TLS ทำงานระหว่างสองอุปกรณ์ที่คุณควบคุม: ทั้งสองฝั่งเชื่อถือใบรับรองเดียวที่คุณสร้างเอง ครอบคลุมการ deploy ทุกรูปแบบที่คุณกำหนดค่าทั้งสองด้านของการเชื่อมต่อ -- Certificate Authority สาธารณะจะเข้ามามีบทบาทเฉพาะเมื่อไคลเอนต์ของบุคคลที่สามต้องเชื่อมต่อ โดยไม่ ถูกบอกให้เชื่อถือใบรับรองที่กำหนดเอง

14.4.3.1. การสร้างใบรับรอง Self-signed

รัน OpenSSL บนเครื่องพัฒนาของคุณ subjectAltName (SAN) คือสิ่งที่ไคลเอนต์ TLS สมัยใหม่ตรวจสอบในระหว่างการตรวจสอบชื่อโฮสต์ ดังนั้นตั้งค่าให้เป็นชื่อโฮสต์และ/หรือที่อยู่ IP ที่ไคลเอนต์จะใช้เพื่อเข้าถึงกล้อง (CN เพียงอย่างเดียวเป็น legacy และถูกละเว้นโดยไคลเอนต์จำนวนมาก) แทนที่ 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"

Note

ใบรับรอง ไคลเอนต์ (ใช้สำหรับการพิสูจน์ตัวตนร่วมกัน ด้านล่าง) ถูกสร้างด้วยคำสั่งเหล่านี้เหมือนกันทุกประการ -- ไม่มีอะไรเฉพาะไคลเอนต์เกี่ยวกับใบรับรองนั้น เพียงสร้างคีย์/ใบรับรองคู่ที่สองที่เป็นอิสระภายใต้ชื่อต่างกัน (เช่น client.key / client.crt) และใช้บนไคลเอนต์ตามที่แสดงในตัวอย่าง mTLS subjectAltName มีความสำคัญเฉพาะสำหรับด้านที่ peer ตรวจสอบชื่อโฮสต์ (ไคลเอนต์ตรวจสอบชื่อของเซิร์ฟเวอร์ ไม่มีอะไรตรวจสอบของไคลเอนต์) ดังนั้นสามารถละเว้นสำหรับใบรับรองไคลเอนต์เท่านั้น -subj / CN เช่นกันเป็นเพียง label บนใบรับรองไคลเอนต์ -- ด้านเซิร์ฟเวอร์ที่นี่ตรวจสอบเฉพาะว่าใบรับรอง chain ไปยัง CA ที่เชื่อถือ ไม่ตรวจสอบชื่อ -- ดังนั้นตั้งค่าให้เป็นอะไรก็ตามที่ระบุไคลเอนต์นั้น (เช่น /CN=sensor-01) เก็บค่า -subj บางค่าไว้เสมอ เพื่อให้ OpenSSL สามารถสร้างใบรับรองแบบไม่โต้ตอบ

อายุใบรับรองถูกตั้งด้วย -days ใบรับรองจะหมดอายุและต้องสร้างและ redeploy ใหม่ก่อนนั้น

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 drive ของ OpenMV Cam หรือด้วย mpremote cp server.der : และ mpremote cp server.key.der : ในฝั่งที่ตรวจสอบ ให้คัดลอกใบรับรอง CA / peer ในรูปแบบ DER ด้วย

ไฟล์ DER ไม่จำเป็นต้องอยู่บนระบบไฟล์ที่เขียนได้ MicroPython ยังสามารถ mount image ROMFS แบบอ่านอย่างเดียวที่ /rom และใบรับรองที่วางไว้ที่นั่นจะโหลดเหมือนกับไฟล์อื่น ๆ -- เช่น ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der") ROMFS image ถูกเตรียมบนเครื่องพัฒนาของคุณและเป็นแบบอ่านอย่างเดียวในขณะ runtime ดังนั้นใบรับรองไม่สามารถถูกแก้ไขบนอุปกรณ์ -- มีประโยชน์สำหรับการล็อกหน่วยผลิตภัณฑ์ โปรดทราบว่าคีย์ส่วนตัวที่จัดเก็บใน ROMFS ยังสามารถอ่านได้โดยโค้ดที่รันบนกล้อง ROMFS ป้องกันการ แก้ไข ไม่ใช่การ ดึงข้อมูล ใบรับรองที่อยู่ใน ROMFS สามารถแทนที่ได้โดยการสร้างและ reflash image ใหม่เท่านั้น

14.4.3.4. การใช้ใบรับรอง

ไคลเอนต์ ที่สมบูรณ์ที่ตั้งนาฬิกา เปิด socket ตรวจสอบเซิร์ฟเวอร์ self-signed และแลกเปลี่ยนข้อมูล:

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 module สำหรับ API ที่สมบูรณ์

Note

ทุกอย่างในหน้านี้ใช้ได้โดยไม่มีการเปลี่ยนแปลงกับ DTLS (TLS ผ่าน UDP) คีย์ ใบรับรอง รูปแบบ DER โมเดลความเชื่อถือ ข้อกังวลเรื่องการหมดอายุ และการเรียก load_cert_chain / load_verify_locations เหมือนกันทุกประการ มีเพียง transport ที่แตกต่าง -- คุณ wrap socket socket.SOCK_DGRAM และเลือก ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER แทน protocol constants ของ TLS สิ่งที่ซับซ้อนเพิ่มเติมอีกอย่างคือ cookie ป้องกันการปลอมแปลงฝั่งเซิร์ฟเวอร์ -- การเชื่อมต่อแรกจากไคลเอนต์ใหม่คาดว่าจะล้มเหลวและไคลเอนต์เพียงแค่ลองใหม่ ดู รองรับ DTLS สำหรับรายละเอียด