ssl --- SSL/TLS モジュール

このモジュールは、ネットワークソケットに対するトランスポート層セキュリティ(以前は広く「Secure Sockets Layer」として知られていました)の暗号化およびピア認証機能へのアクセスを、クライアント側とサーバー側の両方で提供します。

Tip

カメラでの TLS は初めてですか? まずは TLS証明書の取り扱い チュートリアルから始めてください。鍵タイプの選択、証明書の作成とカメラが要求する DER 形式への変換、デバイスへの取り込み、そしてサーバーとクライアントの検証までを、完全に動作する例とともに順を追って解説しています。

注釈

MicroPython は ssl.SSLError を実装していません。SSL/TLS の失敗は代わりに OSError として送出されます。

TLS クライアントで、ファイルシステムに保存された CA 証明書(DER 形式)に対してサーバーの証明書を検証する例:

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

TLS サーバーで、自身の証明書と秘密鍵(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()

関数

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() メソッドを呼び出すというものです。引数 sockserver_sideserver_hostname は変更されずにメソッド呼び出しへ渡されます。引数 do_handshakedo_handshake_on_connect として渡されます。残りの引数は次のような動作になります:

  • cert_reqs は、ピア(サーバーまたはクライアント)が有効な証明書を提示しなければならないかどうかを決定します。ssl.CERT_NONEssl.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 の拡張: certfilekeyfile は文字列の代わりに bytes オブジェクトにすることができ、その場合は実際の証明書/鍵データとして解釈されます。

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

ピアの証明書を検証する CA 証明書チェーンを読み込みます。cafile は CA 証明書のファイルパスです。cadata は CA 証明書を含む bytes オブジェクトです。これらの引数のうち一方のみを指定してください。

get_ciphers() List[str]

有効になっている暗号スイートの一覧を、文字列のリストとして返します。

set_ciphers(ciphers: List[str]) None

このコンテキストで作成されるソケットに対して使用可能な暗号スイートを設定します。ciphersIANA 暗号スイート形式 の文字列リストでなければなりません。

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 は MicroPython 固有の拡張引数で、DTLS サーバーを実装する場合にのみ使用します。詳細は DTLS サポート を参照してください。

警告

既定では証明書の検証は行われません(ssl.CERT_NONE)。安全な接続のためには、cert_reqsSSLContext.verify_modessl.CERT_REQUIRED に設定してピアの証明書を検証する必要があります。そうしないと、接続は中間者攻撃に対して脆弱になります。

CPython の wrap_socketsendrecv などソケットに典型的なメソッドを持つ SSLSocket オブジェクトを返します。MicroPython の wrap_socket は、これらのソケットメソッドを持たない、CPython の SSLObject により近いオブジェクトを返します。

verify_mode

ピア証明書の検証の動作を設定または取得します。CERT_* 定数のいずれかでなければなりません。

注釈

ssl.CERT_REQUIRED は、mpremote rtc --setntptime などを使用してデバイスの日時が正しく設定されていることを必要とし、またクライアント側では server_hostname を指定する必要があります。

DTLS サポート

CPython との相違点

これは MicroPython の拡張です。

このモジュールは、SSLContextprotocol 引数として使用できる PROTOCOL_DTLS_CLIENT および PROTOCOL_DTLS_SERVER 定数を介して、クライアントモードとサーバーモードの DTLS をサポートします。

この場合、基盤となるソケットはデータグラムソケットとして振る舞うことが期待されます(すなわち afsocket.AF_INETtypesocket.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 呼び出しは OSError エラー「Hello Verify Required」で失敗します。これは DTLS の「Hello Verify」クッキーがまだクライアントに知られていないためです。同じクライアントが 2 回目に接続すれば wrap_socket は成功します。

  • 「Hello Verify」用の DTLS クッキーは SSLContext オブジェクトに関連付けられているため、同じクライアントからの後続の接続をラップするには同じ SSLContext オブジェクトを使用すべきです。クッキーの実装にはタイムアウトが含まれ、接続するクライアント数に関係なくメモリ使用量は一定であるため、サーバーの存続期間を通じて同じ SSLContext オブジェクトを再利用しても問題ありません。

定数

ssl.PROTOCOL_TLS_CLIENT: int

protocol パラメータでサポートされる値で、TLS クライアントモードを選択します。

ssl.PROTOCOL_TLS_SERVER: int

protocol パラメータでサポートされる値で、TLS サーバーモードを選択します。

ssl.PROTOCOL_DTLS_CLIENT: int

protocol パラメータでサポートされる値で、DTLS クライアントモードを選択します。

ssl.PROTOCOL_DTLS_SERVER: int

protocol パラメータでサポートされる値で、DTLS サーバーモードを選択します。

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 属性でサポートされる値です。ピアからの有効な証明書が必要です。