14.4.3. 自己署名証明書

自己署名証明書は、自分が管理する2台のデバイス間でTLSを動作させる最も手早い方法です。両端が、自分で生成した単一の証明書を信頼します。これは接続の両側を自分で構成するあらゆるデプロイをカバーします。サードパーティのクライアントが、カスタム証明書を信頼するよう指示されずに接続しなければならない場合に初めて、公開認証局(CA)が登場します。

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)で2つ目の独立した鍵/証明書のペアを生成し、mTLSの例で示すようにクライアント側で使用するだけです。subjectAltName が意味を持つのは、ピアがそのホスト名を検証する側のみです(クライアントはサーバーの名前をチェックしますが、クライアントの名前は何もチェックしません)。したがってクライアント専用の証明書では省略できます。同様に -subj / CN もクライアント証明書では単なるラベルにすぎません。ここでのサーバー側は証明書が信頼されたCAまでチェーンするかどうかだけを確認し、名前を照合することは一切ありません。そのため、そのクライアントを識別する任意の値(例えば /CN=sensor-01)に設定してください。OpenSSLが非対話的に証明書を生成できるよう、いずれにせよ何らかの -subj 値は指定しておいてください。

証明書の有効期間は -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(UDP上のTLS)にも変更なくそのまま当てはまります。鍵、証明書、DER形式、信頼モデル、有効期限への配慮、そして load_cert_chain / load_verify_locations の呼び出しは同一であり、異なるのはトランスポートだけです。socket.SOCK_DGRAM ソケットをラップし、TLS プロトコル定数の代わりに ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER を選択します。唯一の追加の特殊事情は、サーバー側のなりすまし防止クッキーです。新しいクライアントからの最初の接続は失敗することが想定されており、クライアントは単に再試行します。詳細は DTLS サポート を参照してください。