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()メソッドを呼び出すというものです。引数 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 は MicroPython 固有の拡張引数で、DTLS サーバーを実装する場合にのみ使用します。詳細は 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呼び出しはOSErrorエラー「Hello Verify Required」で失敗します。これは DTLS の「Hello Verify」クッキーがまだクライアントに知られていないためです。同じクライアントが 2 回目に接続すれば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属性でサポートされる値です。ピアからの有効な証明書が必要です。