ssl — TLS/SSL 套接字物件包裝器

原始碼: Lib/ssl.py


本模組提供了對傳輸層安全(通常稱為“安全套接字層”)加密和對等身份驗證功能的訪問,用於客戶端和伺服器端的網路套接字。本模組使用 OpenSSL 庫。只要在平臺上安裝了 OpenSSL,它就可在所有現代 Unix 系統、Windows、macOS 以及可能的其他平臺上使用。

備註

一些行為可能取決於平臺,因為會呼叫作業系統套接字 API。安裝的 OpenSSL 版本也可能導致行為差異。例如,TLSv1.3 隨 OpenSSL 版本 1.1.1 一起提供。

警告

請在閱讀 安全注意事項 之前,請勿使用本模組。這樣做可能會產生虛假的安全感,因為 ssl 模組的預設設定不一定適合您的應用程式。

可用性:非 WASI。

此模組在 WebAssembly 上不起作用或不可用。有關更多資訊,請參閱 WebAssembly 平臺

本節介紹 ssl 模組中的物件和函式;有關 TLS、SSL 和證書的更一般資訊,請參閱底部的“另請參閱”部分中的文件。

本模組提供了一個類 ssl.SSLSocket,它繼承自 socket.socket 型別,並提供了一個類似套接字的包裝器,該包裝器還可以對透過套接字傳輸的資料進行 SSL 加密和解密。它支援其他方法,例如 getpeercert()(用於檢索連線另一方的證書)、cipher()(用於檢索安全連線正在使用的密碼套件)或 get_verified_chain()get_unverified_chain()(用於檢索證書鏈)。

對於更復雜的應用程式,ssl.SSLContext 類有助於管理設定和證書,然後可以透過 SSLContext.wrap_socket() 方法建立的 SSL 套接字繼承這些設定。

3.5.3 版本更改: 更新以支援與 OpenSSL 1.1.0 連結

3.6 版本更改: OpenSSL 0.9.8、1.0.0 和 1.0.1 已棄用,不再支援。將來,ssl 模組將至少需要 OpenSSL 1.0.2 或 1.1.0。

3.10 版本更改: 已實現 PEP 644。ssl 模組需要 OpenSSL 1.1.1 或更新版本。

使用已棄用的常量和函式會導致棄用警告。

函式、常量和異常

套接字建立

SSLSocket 的例項必須使用 SSLContext.wrap_socket() 方法建立。輔助函式 create_default_context() 返回一個新的上下文,並具有安全預設設定。

使用預設上下文和 IPv4/IPv6 雙棧的客戶端套接字示例

import socket
import ssl

hostname = 'www.python.org'
context = ssl.create_default_context()

with socket.create_connection((hostname, 443)) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())

使用自定義上下文和 IPv4 的客戶端套接字示例

hostname = 'www.python.org'
# PROTOCOL_TLS_CLIENT requires valid cert chain and hostname
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('path/to/cabundle.pem')

with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
    with context.wrap_socket(sock, server_hostname=hostname) as ssock:
        print(ssock.version())

在 localhost IPv4 上監聽的伺服器套接字示例

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('/path/to/certchain.pem', '/path/to/private.key')

with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
    sock.bind(('127.0.0.1', 8443))
    sock.listen(5)
    with context.wrap_socket(sock, server_side=True) as ssock:
        conn, addr = ssock.accept()
        ...

上下文建立

一個方便的函式,用於為常見用途建立 SSLContext 物件。

ssl.create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, capath=None, cadata=None)

返回一個新的 SSLContext 物件,並具有給定 purpose 的預設設定。這些設定由 ssl 模組選擇,通常代表比直接呼叫 SSLContext 建構函式更高的安全級別。

cafilecapathcadata 代表用於證書驗證的可選 CA 證書,與 SSLContext.load_verify_locations() 中的 cafile 引數格式相同。如果所有三個引數都是 None,則此函式可以選擇信任系統預設的 CA 證書。

設定包括:PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVEROP_NO_SSLv2,以及 OP_NO_SSLv3,並帶有高加密強度的密碼套件,不含 RC4 且不含未經身份驗證的密碼套件。將 SERVER_AUTH 作為 purpose 傳遞會設定 verify_modeCERT_REQUIRED,並載入 CA 證書(當 cafilecapathcadata 中至少有一個給出時)或使用 SSLContext.load_default_certs() 載入預設 CA 證書。

keylog_filename 受支援且環境變數 SSLKEYLOGFILE 已設定時,create_default_context() 會啟用金鑰記錄。

此上下文的預設設定包括 VERIFY_X509_PARTIAL_CHAINVERIFY_X509_STRICT。這些設定使底層的 OpenSSL 實現更符合 RFC 5280 的要求,但可能會與舊的 X.509 證書存在一些不相容性。

備註

協議、選項、密碼套件和其他設定可能會隨時更改為更嚴格的值,恕不另行通知。這些值在相容性和安全性之間取得了合理的平衡。

如果您的應用程式需要特定的設定,您應該建立 SSLContext 並自行應用這些設定。

備註

如果您發現當某些舊客戶端或伺服器嘗試使用此函式建立的 SSLContext 連線時出現“協議或密碼套件不匹配”的錯誤,可能是因為它們僅支援 SSL3.0,而此函式透過 OP_NO_SSLv3 停用了它。SSL3.0 被廣泛認為已經 完全失效。如果您仍希望繼續使用此函式但仍允許 SSL 3.0 連線,您可以使用以下方法重新啟用它們:

ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
ctx.options &= ~ssl.OP_NO_SSLv3

備註

此上下文預設啟用 VERIFY_X509_STRICT,這可能會拒絕底層 OpenSSL 實現原本會接受的 RFC 5280 之前的或格式錯誤的證書。雖然不建議停用此設定,但您可以使用以下方法進行停用:

ctx = ssl.create_default_context()
ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT

在 3.4 版本加入。

3.4.4 版本更改: RC4 已從預設密碼字串中移除。

3.6 版本更改: ChaCha20/Poly1305 已新增到預設密碼字串。

3DES 已從預設密碼字串中移除。

3.8 版本更改: 添加了對向 SSLKEYLOGFILE 記錄金鑰的支援。

3.10 版本更改: 上下文現在使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 協議,而不是通用的 PROTOCOL_TLS

3.13 版本更改: 上下文現在在其預設驗證標誌中使用 VERIFY_X509_PARTIAL_CHAINVERIFY_X509_STRICT

異常

exception ssl.SSLError

當底層 SSL 實現(當前由 OpenSSL 庫提供)發生錯誤時引發。這表示疊加在底層網路連線之上的更高級別的加密和身份驗證層存在某種問題。此錯誤是 OSError 的子類。SSLError 例項的錯誤程式碼和訊息由 OpenSSL 庫提供。

3.3 版本更改: SSLError 曾是 socket.error 的子類。

library

一個字串助記符,指示發生錯誤的 OpenSSL 子模組,例如 SSLPEMX509。可能值的範圍取決於 OpenSSL 版本。

在 3.3 版本加入。

reason

一個字串助記符,指示發生此錯誤的原因,例如 CERTIFICATE_VERIFY_FAILED。可能值的範圍取決於 OpenSSL 版本。

在 3.3 版本加入。

exception ssl.SSLZeroReturnError

當嘗試讀寫時 SSL 連線已乾淨關閉,則引發 SSLError 的子類。請注意,這並不意味著底層傳輸(讀取的 TCP)已關閉。

在 3.3 版本加入。

exception ssl.SSLWantReadError

非阻塞 SSL 套接字 在嘗試讀寫資料時引發的 SSLError 的子類,但需要從底層 TCP 傳輸接收更多資料才能完成請求。

在 3.3 版本加入。

exception ssl.SSLWantWriteError

非阻塞 SSL 套接字 在嘗試讀寫資料時引發的 SSLError 的子類,但需要向底層 TCP 傳輸傳送更多資料才能完成請求。

在 3.3 版本加入。

exception ssl.SSLSyscallError

SSLError 的子類,在嘗試在 SSL 套接字上執行操作時遇到系統錯誤時引發。不幸的是,沒有簡單的方法可以檢查原始 errno 號碼。

在 3.3 版本加入。

exception ssl.SSLEOFError

SSLError 的子類,當 SSL 連線被突然終止時引發。通常,遇到此錯誤時不應嘗試重用底層傳輸。

在 3.3 版本加入。

exception ssl.SSLCertVerificationError

當證書驗證失敗時,引發 SSLError 的子類。

在 3.7 版本加入。

verify_code

表示驗證錯誤的數字錯誤程式碼。

verify_message

表示驗證錯誤的易讀字串。

exception ssl.CertificateError

SSLCertVerificationError 的別名。

3.7 版本更改: 此異常現在是 SSLCertVerificationError 的別名。

隨機數生成

ssl.RAND_bytes(num, /)

返回 num 個加密安全的偽隨機位元組。如果 PRNG 未使用足夠的資料進行播種,或者當前 RAND 方法不支援該操作,則引發 SSLErrorRAND_status() 可用於檢查 PRNG 的狀態,而 RAND_add() 可用於播種 PRNG。

對於幾乎所有應用程式,os.urandom() 是更好的選擇。

閱讀維基百科文章 加密安全的偽隨機數生成器 (CSPRNG),以瞭解加密強生成器的要求。

在 3.3 版本加入。

ssl.RAND_status()

如果 SSL 偽隨機數生成器已使用“足夠”的隨機性進行了播種,則返回 True,否則返回 False。您可以使用 ssl.RAND_egd()ssl.RAND_add() 來增加偽隨機數生成器的隨機性。

ssl.RAND_add(bytes, entropy, /)

將給定的 bytes 混合到 SSL 偽隨機數生成器中。引數 entropy(一個浮點數)是字串中熵的下限(因此您始終可以使用 0.0)。有關熵源的更多資訊,請參閱 RFC 1750

3.5 版本中修改: 現在接受可寫 bytes-like object

證書處理

ssl.cert_time_to_seconds(cert_time)

給定證書中的“notBefore”或“notAfter”日期的 cert_time 字串(採用 "%b %d %H:%M:%S %Y %Z" strptime 格式(C 語言環境)),返回自 Epoch 以來的秒數。

這是一個例子

>>> import ssl
>>> timestamp = ssl.cert_time_to_seconds("Jan  5 09:34:43 2018 GMT")
>>> timestamp
1515144883
>>> from datetime import datetime
>>> print(datetime.utcfromtimestamp(timestamp))
2018-01-05 09:34:43

“notBefore”或“notAfter”日期必須使用 GMT(RFC 5280)。

3.5 版本更改: 將輸入時間解釋為 UTC 時間,如輸入字串中的“GMT”時區所示。以前使用的是本地時區。返回一個整數(輸入格式中沒有秒的小數部分)

ssl.get_server_certificate(addr, ssl_version=PROTOCOL_TLS_CLIENT, ca_certs=None[, timeout])

給定一個 SSL 保護的伺服器地址 addr,其格式為 (hostname, port-number) 對,則獲取伺服器的證書,並將其作為 PEM 編碼的字串返回。如果指定了 ssl_version,則使用該版本的 SSL 協議嘗試連線到伺服器。如果指定了 ca_certs,它應該是一個包含根證書列表的檔案,格式與 SSLContext.load_verify_locations() 中的 cafile 引數相同。此呼叫將嘗試根據該根證書集驗證伺服器證書,如果驗證嘗試失敗,則會引發錯誤。可以使用 timeout 引數指定超時。

3.3 版本更改: 此函式現在支援 IPv6。

3.5 版本更改: 為了與現代伺服器最大程度相容,預設的 ssl_versionPROTOCOL_SSLv3 更改為 PROTOCOL_TLS

3.10 版本更改: 添加了 timeout 引數。

ssl.DER_cert_to_PEM_cert(der_cert_bytes)

給定一個 DER 編碼的位元組串格式的證書,返回該證書的 PEM 編碼字串版本。

ssl.PEM_cert_to_DER_cert(pem_cert_string)

給定一個 ASCII PEM 字串格式的證書,返回該證書的 DER 編碼位元組序列。

ssl.get_default_verify_paths()

返回一個命名元組,其中包含 OpenSSL 的預設 cafile 和 capath 的路徑。這些路徑與 SSLContext.set_default_verify_paths() 使用的路徑相同。返回值是一個名為 DefaultVerifyPaths命名元組

  • cafile - cafile 的解析路徑,如果檔案不存在則為 None

  • capath - capath 的解析路徑,如果目錄不存在則為 None

  • openssl_cafile_env - 指向 cafile 的 OpenSSL 環境變數鍵,

  • openssl_cafile - cafile 的硬編碼路徑,

  • openssl_capath_env - 指向 capath 的 OpenSSL 環境變數鍵,

  • openssl_capath - capath 目錄的硬編碼路徑

在 3.4 版本加入。

ssl.enum_certificates(store_name)

從 Windows 的系統證書儲存中檢索證書。store_name 可以是 CAROOTMY。Windows 也可能提供其他證書儲存。

該函式返回 (cert_bytes, encoding_type, trust) 元組列表。encoding_type 指定 cert_bytes 的編碼。對於 X.509 ASN.1 資料,它是 x509_asn,對於 PKCS#7 ASN.1 資料,它是 pkcs_7_asn。Trust 指定證書的目的,作為 OID 集合或確切地 True(如果證書對所有目的都可信)。

示例

>>> ssl.enum_certificates("CA")
[(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}),
 (b'data...', 'x509_asn', True)]

可用性: Windows。

在 3.4 版本加入。

ssl.enum_crls(store_name)

從 Windows 的系統證書儲存中檢索 CRL。store_name 可以是 CAROOTMY。Windows 也可能提供其他證書儲存。

該函式返回 (cert_bytes, encoding_type, trust) 元組列表。encoding_type 指定 cert_bytes 的編碼。對於 X.509 ASN.1 資料,它是 x509_asn,對於 PKCS#7 ASN.1 資料,它是 pkcs_7_asn

可用性: Windows。

在 3.4 版本加入。

常量

所有常量現在都是 enum.IntEnumenum.IntFlag 集合。

在 3.6 版本加入。

ssl.CERT_NONE

SSLContext.verify_mode 的可能值。除了 PROTOCOL_TLS_CLIENT 之外,它是預設模式。對於客戶端套接字,幾乎所有證書都會被接受。驗證錯誤,如不受信任或過期的證書,將被忽略,不會中止 TLS/SSL 握手。

在伺服器模式下,不會向客戶端請求證書,因此客戶端不會發送任何證書來進行客戶端證書身份驗證。

請參閱下文 安全注意事項 的討論。

ssl.CERT_OPTIONAL

SSLContext.verify_mode 的可能值。在客戶端模式下,CERT_OPTIONAL 的含義與 CERT_REQUIRED 相同。建議在客戶端套接字上改用 CERT_REQUIRED

在伺服器模式下,會向客戶端傳送客戶端證書請求。客戶端可以選擇忽略該請求或傳送證書以執行 TLS 客戶端證書身份驗證。如果客戶端選擇傳送證書,則會對其進行驗證。任何驗證錯誤將立即中止 TLS 握手。

使用此設定需要在 SSLContext.load_verify_locations() 中提供有效的 CA 證書集。

ssl.CERT_REQUIRED

SSLContext.verify_mode 的可能值。在此模式下,套接字連線的另一方必須提供證書;如果沒有提供證書,或者其驗證失敗,則會引發 SSLError。此模式 **不** 足以在客戶端模式下驗證證書,因為它不匹配主機名。必須同時啟用 check_hostname 才能驗證證書的真實性。PROTOCOL_TLS_CLIENT 使用 CERT_REQUIRED 並預設啟用 check_hostname

使用伺服器套接字時,此模式提供強制性的 TLS 客戶端證書身份驗證。會向客戶端傳送客戶端證書請求,客戶端必須提供一個有效且受信任的證書。

使用此設定需要在 SSLContext.load_verify_locations() 中提供有效的 CA 證書集。

class ssl.VerifyMode

CERT_* 常量的 enum.IntEnum 集合。

在 3.6 版本加入。

ssl.VERIFY_DEFAULT

SSLContext.verify_flags 的可能值。在此模式下,不檢查證書吊銷列表 (CRL)。預設情況下,OpenSSL 既不要求也不驗證 CRL。

在 3.4 版本加入。

ssl.VERIFY_CRL_CHECK_LEAF

SSLContext.verify_flags 的可能值。在此模式下,只檢查對等證書,而不檢查任何中間 CA 證書。該模式需要由對等證書的頒發者(其直接上級 CA)簽名的有效 CRL。如果未使用 SSLContext.load_verify_locations 載入正確的 CRL,驗證將失敗。

在 3.4 版本加入。

ssl.VERIFY_CRL_CHECK_CHAIN

SSLContext.verify_flags 的可能值。在此模式下,將檢查對等證書鏈中所有證書的 CRL。

在 3.4 版本加入。

ssl.VERIFY_X509_STRICT

SSLContext.verify_flags 的可能值,用於停用對損壞的 X.509 證書的錯誤修復。

在 3.4 版本加入。

ssl.VERIFY_ALLOW_PROXY_CERTS

SSLContext.verify_flags 的可能值,用於啟用代理證書驗證。

在 3.10 版本加入。

ssl.VERIFY_X509_TRUSTED_FIRST

SSLContext.verify_flags 的可能值。它指示 OpenSSL 在構建信任鏈以驗證證書時優先使用受信任的證書。此標誌預設啟用。

添加於 3.4.4 版本。

ssl.VERIFY_X509_PARTIAL_CHAIN

SSLContext.verify_flags 的可能值。它指示 OpenSSL 接受信任儲存中的中間 CA,如同自簽名根 CA 證書一樣。這使得無需信任中間 CA 的上級根 CA 即可信任由中間 CA 頒發的證書。

在 3.10 版本加入。

class ssl.VerifyFlags

VERIFY_* 常量的 enum.IntFlag 集合。

在 3.6 版本加入。

ssl.PROTOCOL_TLS

選擇客戶端和伺服器都支援的最高協議版本。儘管名稱如此,此選項可以同時選擇“SSL”和“TLS”協議。

在 3.6 版本加入。

3.10 版本已棄用: TLS 客戶端和伺服器需要不同的預設設定才能安全通訊。通用的 TLS 協議常量已棄用,建議使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER

ssl.PROTOCOL_TLS_CLIENT

自動協商客戶端和伺服器都支援的最高協議版本,併為上下文配置客戶端連線。該協議預設啟用 CERT_REQUIREDcheck_hostname

在 3.6 版本加入。

ssl.PROTOCOL_TLS_SERVER

自動協商客戶端和伺服器都支援的最高協議版本,併為上下文配置伺服器端連線。

在 3.6 版本加入。

ssl.PROTOCOL_SSLv23

PROTOCOL_TLS 的別名。

3.6 版本已棄用: 使用 PROTOCOL_TLS 代替。

ssl.PROTOCOL_SSLv3

選擇 SSL 版本 3 作為通道加密協議。

如果 OpenSSL 編譯時使用了 no-ssl3 選項,則此協議不可用。

警告

SSL 版本 3 不安全。強烈建議不要使用它。

3.6 版本已棄用: OpenSSL 已棄用所有版本特定的協議。請使用預設協議 PROTOCOL_TLS_SERVERPROTOCOL_TLS_CLIENT,並結合 SSLContext.minimum_versionSSLContext.maximum_version

ssl.PROTOCOL_TLSv1

選擇 TLS 版本 1.0 作為通道加密協議。

3.6 版本已棄用: OpenSSL 已棄用所有版本特定的協議。

ssl.PROTOCOL_TLSv1_1

選擇 TLS 版本 1.1 作為通道加密協議。僅在使用 openssl 版本 1.0.1+ 時可用。

在 3.4 版本加入。

3.6 版本已棄用: OpenSSL 已棄用所有版本特定的協議。

ssl.PROTOCOL_TLSv1_2

選擇 TLS 版本 1.2 作為通道加密協議。僅在使用 openssl 版本 1.0.1+ 時可用。

在 3.4 版本加入。

3.6 版本已棄用: OpenSSL 已棄用所有版本特定的協議。

ssl.OP_ALL

啟用對其他 SSL 實現中存在的各種錯誤的修復。此選項預設設定。它不一定設定與 OpenSSL 的 SSL_OP_ALL 常量相同的標誌。

在 3.2 版本加入。

ssl.OP_NO_SSLv2

阻止 SSLv2 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 SSLv2 作為協議版本。

在 3.2 版本加入。

3.6 版本已棄用: SSLv2 已棄用

ssl.OP_NO_SSLv3

阻止 SSLv3 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 SSLv3 作為協議版本。

在 3.2 版本加入。

3.6 版本已棄用: SSLv3 已棄用

ssl.OP_NO_TLSv1

阻止 TLSv1 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 TLSv1 作為協議版本。

在 3.2 版本加入。

3.7 版本已棄用: 該選項自 OpenSSL 1.1.0 起已棄用,請使用新的 SSLContext.minimum_versionSSLContext.maximum_version 代替。

ssl.OP_NO_TLSv1_1

阻止 TLSv1.1 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 TLSv1.1 作為協議版本。僅在使用 openssl 版本 1.0.1+ 時可用。

在 3.4 版本加入。

3.7 版本已棄用: 該選項自 OpenSSL 1.1.0 起已棄用。

ssl.OP_NO_TLSv1_2

阻止 TLSv1.2 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 TLSv1.2 作為協議版本。僅在使用 openssl 版本 1.0.1+ 時可用。

在 3.4 版本加入。

3.7 版本已棄用: 該選項自 OpenSSL 1.1.0 起已棄用。

ssl.OP_NO_TLSv1_3

阻止 TLSv1.3 連線。此選項僅與 PROTOCOL_TLS 結合使用。它阻止對等方選擇 TLSv1.3 作為協議版本。TLS 1.3 在 OpenSSL 1.1.1 或更高版本中可用。當 Python 編譯到較舊版本的 OpenSSL 時,此標誌預設為 0

添加於 3.6.3 版本。

3.7 版本已棄用: 該選項自 OpenSSL 1.1.0 起已棄用。它已新增到 2.7.15 和 3.6.3 以與 OpenSSL 1.0.2 向後相容。

ssl.OP_NO_RENEGOTIATION

停用 TLSv1.2 及更早版本中的所有重新協商。不傳送 HelloRequest 訊息,並忽略透過 ClientHello 進行的重新協商請求。

此選項僅在使用 OpenSSL 1.1.0h 及更高版本時可用。

在 3.7 版本加入。

ssl.OP_CIPHER_SERVER_PREFERENCE

使用伺服器的密碼套件排序偏好,而不是客戶端的。此選項對客戶端套接字和 SSLv2 伺服器套接字無效。

在 3.3 版本加入。

ssl.OP_SINGLE_DH_USE

防止為不同的 SSL 會話重用相同的 DH 金鑰。這可以提高前向保密性,但需要更多的計算資源。此選項僅適用於伺服器套接字。

在 3.3 版本加入。

ssl.OP_SINGLE_ECDH_USE

防止為不同的 SSL 會話重用相同的 ECDH 金鑰。這可以提高前向保密性,但需要更多的計算資源。此選項僅適用於伺服器套接字。

在 3.3 版本加入。

ssl.OP_ENABLE_MIDDLEBOX_COMPAT

在 TLS 1.3 握手中傳送虛擬的 Change Cipher Spec (CCS) 訊息,使 TLS 1.3 連線看起來更像 TLS 1.2 連線。

此選項僅在使用 OpenSSL 1.1.1 及更高版本時可用。

在 3.8 版本加入。

ssl.OP_NO_COMPRESSION

停用 SSL 通道上的壓縮。如果應用程式協議支援自己的壓縮方案,則此功能很有用。

在 3.3 版本加入。

class ssl.Options

OP_* 常量的 enum.IntFlag 集合。

ssl.OP_NO_TICKET

阻止客戶端請求會話票證。

在 3.6 版本加入。

ssl.OP_IGNORE_UNEXPECTED_EOF

忽略 TLS 連線的意外關閉。

此選項僅在使用 OpenSSL 3.0.0 及更高版本時可用。

在 3.10 版本加入。

ssl.OP_ENABLE_KTLS

啟用核心 TLS 的使用。為了受益於此功能,OpenSSL 必須已編譯支援它,並且協商的密碼套件和擴充套件必須得到它的支援(支援的列表可能因平臺和核心版本而異)。

請注意,在使用核心 TLS 時,某些加密操作由核心直接執行,而不是透過任何可用的 OpenSSL Providers 執行。如果應用程式要求所有加密操作都由 FIPS provider 執行,這可能是不可取的。

此選項僅在使用 OpenSSL 3.0.0 及更高版本時可用。

3.12 新版功能.

ssl.OP_LEGACY_SERVER_CONNECT

僅允許 OpenSSL 和未修補的伺服器之間的遺留不安全重新協商。

3.12 新版功能.

ssl.HAS_ALPN

OpenSSL 庫是否內建支援 應用程式層協議協商 TLS 擴充套件,如 RFC 7301 所述。

在 3.5 版本加入。

ssl.HAS_NEVER_CHECK_COMMON_NAME

OpenSSL 庫是否內建支援不檢查主題通用名稱,並且 SSLContext.hostname_checks_common_name 是可寫的。

在 3.7 版本加入。

ssl.HAS_ECDH

OpenSSL 庫是否內建支援基於橢圓曲線的 Diffie-Hellman 金鑰交換。除非分發者明確停用了該功能,否則這應為 true。

在 3.3 版本加入。

ssl.HAS_SNI

OpenSSL 庫是否內建支援 伺服器名稱指示 擴充套件(如 RFC 6066 中所述)。

在 3.2 版本加入。

ssl.HAS_NPN

OpenSSL 庫是否內建支援 下一協議協商,如 應用程式層協議協商 中所述。如果為 true,您可以使用 SSLContext.set_npn_protocols() 方法來宣告您希望支援的協議。

在 3.3 版本加入。

ssl.HAS_SSLv2

OpenSSL 庫是否內建支援 SSL 2.0 協議。

在 3.7 版本加入。

ssl.HAS_SSLv3

OpenSSL 庫是否內建支援 SSL 3.0 協議。

在 3.7 版本加入。

ssl.HAS_TLSv1

OpenSSL 庫是否內建支援 TLS 1.0 協議。

在 3.7 版本加入。

ssl.HAS_TLSv1_1

OpenSSL 庫是否內建支援 TLS 1.1 協議。

在 3.7 版本加入。

ssl.HAS_TLSv1_2

OpenSSL 庫是否內建支援 TLS 1.2 協議。

在 3.7 版本加入。

ssl.HAS_TLSv1_3

OpenSSL 庫是否內建支援 TLS 1.3 協議。

在 3.7 版本加入。

ssl.HAS_PSK

OpenSSL 庫是否內建支援 TLS-PSK。

在 3.13 版本加入。

ssl.HAS_PHA

OpenSSL 庫是否內建支援 TLS-PHA。

在 3.14 版本加入。

ssl.CHANNEL_BINDING_TYPES

支援的 TLS 通道繫結型別列表。此列表中的字串可用作 SSLSocket.get_channel_binding() 的引數。

在 3.3 版本加入。

ssl.OPENSSL_VERSION

直譯器載入的 OpenSSL 庫的版本字串。

>>> ssl.OPENSSL_VERSION
'OpenSSL 1.0.2k  26 Jan 2017'

在 3.2 版本加入。

ssl.OPENSSL_VERSION_INFO

一個由五個整陣列成的元組,表示 OpenSSL 庫的版本資訊。

>>> ssl.OPENSSL_VERSION_INFO
(1, 0, 2, 11, 15)

在 3.2 版本加入。

ssl.OPENSSL_VERSION_NUMBER

OpenSSL 庫的原始版本號,表示為一個整數。

>>> ssl.OPENSSL_VERSION_NUMBER
268443839
>>> hex(ssl.OPENSSL_VERSION_NUMBER)
'0x100020bf'

在 3.2 版本加入。

ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE
ssl.ALERT_DESCRIPTION_INTERNAL_ERROR
ALERT_DESCRIPTION_*

來自 RFC 5246 及其他標準的告警描述。 IANA TLS 告警登錄檔 包含此列表以及定義其含義的 RFC 的連結。

SSLContext.set_servername_callback() 中用作回撥函式的返回值。

在 3.4 版本加入。

class ssl.AlertDescription

enum.IntEnum 集合,包含 ALERT_DESCRIPTION_* 常量。

在 3.6 版本加入。

Purpose.SERVER_AUTH

用於 create_default_context()SSLContext.load_default_certs() 的選項。此值表示上下文可用於驗證 Web 伺服器(因此,它將用於建立客戶端套接字)。

在 3.4 版本加入。

Purpose.CLIENT_AUTH

用於 create_default_context()SSLContext.load_default_certs() 的選項。此值表示上下文可用於驗證 Web 客戶端(因此,它將用於建立伺服器端套接字)。

在 3.4 版本加入。

class ssl.SSLErrorNumber

enum.IntEnum 集合,包含 SSL_ERROR_* 常量。

在 3.6 版本加入。

class ssl.TLSVersion

用於 SSLContext.maximum_versionSSLContext.minimum_version 的 SSL 和 TLS 版本 enum.IntEnum 集合。

在 3.7 版本加入。

TLSVersion.MINIMUM_SUPPORTED
TLSVersion.MAXIMUM_SUPPORTED

支援的最低或最高 SSL 或 TLS 版本。這些是魔法常量。它們的值不反映最低和最高的可用 TLS/SSL 版本。

TLSVersion.SSLv3
TLSVersion.TLSv1
TLSVersion.TLSv1_1
TLSVersion.TLSv1_2
TLSVersion.TLSv1_3

SSL 3.0 到 TLS 1.3。

版本 3.10 已棄用: 除了 TLSVersion.TLSv1_2TLSVersion.TLSv1_3 之外,所有 TLSVersion 成員均已棄用。

SSL 套接字

class ssl.SSLSocket(socket.socket)

SSL 套接字提供 套接字物件 的以下方法:

然而,由於 SSL(和 TLS)協議在 TCP 之上具有自己的幀協議,SSL 套接字抽象在某些方面可能與普通作業系統級套接字的規範有所不同。請特別參閱 關於非阻塞套接字的說明

SSLSocket 的例項必須使用 SSLContext.wrap_socket() 方法建立。

版本 3.5 已更改: 添加了 sendfile() 方法。

版本 3.5 已更改: shutdown() 不再每次接收或傳送位元組時重置套接字超時。套接字超時現在是關閉的總最長時間。

版本 3.6 已棄用: 直接建立 SSLSocket 例項是過時的,請使用 SSLContext.wrap_socket() 來包裝套接字。

版本 3.7 已更改: SSLSocket 例項必須透過 wrap_socket() 建立。在早期版本中,可以直接建立例項。這從未被記錄或官方支援。

版本 3.10 已更改: Python 現在內部使用 SSL_read_exSSL_write_ex。這些函式支援讀寫大於 2 GB 的資料。寫入零長度資料不再因協議違規錯誤而失敗。

SSL 套接字還具有以下附加方法和屬性:

SSLSocket.read(len=1024, buffer=None)

從 SSL 套接字讀取最多 len 位元組的資料,並將結果作為 bytes 例項返回。如果指定了 buffer,則改用緩衝區讀取,並返回讀取的位元組數。

如果套接字是 非阻塞 且讀取會阻塞,則引發 SSLWantReadErrorSSLWantWriteError

由於隨時可能進行重新協商,呼叫 read() 也可能導致寫入操作。

版本 3.5 已更改: 套接字超時不再每次接收或傳送位元組時重置。套接字超時現在是讀取最多 len 位元組的總最長時間。

版本 3.6 已棄用: 使用 recv() 而不是 read()

SSLSocket.write(data)

data 寫入 SSL 套接字,並返回寫入的位元組數。data 引數必須是支援緩衝區介面的物件。

如果套接字是 非阻塞 且寫入會阻塞,則引發 SSLWantReadErrorSSLWantWriteError

由於隨時可能進行重新協商,呼叫 write() 也可能導致讀取操作。

版本 3.5 已更改: 套接字超時不再每次接收或傳送位元組時重置。套接字超時現在是寫入 data 的總最長時間。

版本 3.6 已棄用: 使用 send() 而不是 write()

備註

read()write() 方法是低階方法,它們讀取和寫入未加密的應用層資料,並將其解密/加密為加密的線路上層資料。這些方法需要活動的 SSL 連線,即握手已完成且未呼叫 SSLSocket.unwrap()

通常,您應該使用 recv()send() 等套接字 API 方法,而不是這些方法。

SSLSocket.do_handshake(block=False)

執行 SSL 設定握手。

如果 block 為 true 且 gettimeout() 返回的超時為零,則套接字將被設定為阻塞模式,直到完成握手。

版本 3.4 已更改: 當套接字的 context 屬性的 check_hostname 屬性為 true 時,握手方法還會執行 match_hostname()

版本 3.5 已更改: 套接字超時不再每次接收或傳送位元組時重置。套接字超時現在是握手的總最長時間。

版本 3.7 已更改: 主機名或 IP 地址由 OpenSSL 在握手期間進行匹配。不再使用 match_hostname() 函式。如果 OpenSSL 拒絕主機名或 IP 地址,握手將提前中止,並向對端傳送 TLS 告警訊息。

SSLSocket.getpeercert(binary_form=False)

如果連線的另一端沒有證書,則返回 None。如果 SSL 握手尚未完成,則引發 ValueError

如果 binary_form 引數為 False,並且從對端收到了證書,則此方法返回一個 dict 例項。如果證書未經驗證,則 dict 為空。如果證書已驗證,則返回一個具有多個鍵的 dict,其中包含 subject(證書頒發給的主體)和 issuer(頒發證書的主體)。如果證書包含“主體備用名稱”擴充套件(參見 RFC 3280),則 dict 中還會有一個 subjectAltName 鍵。

subjectissuer 欄位是元組,包含證書資料結構中相應欄位的相對區分名稱 (RDN) 序列,每個 RDN 是一個名稱-值對序列。以下是一個真實世界的例子:

{'issuer': ((('countryName', 'IL'),),
            (('organizationName', 'StartCom Ltd.'),),
            (('organizationalUnitName',
              'Secure Digital Certificate Signing'),),
            (('commonName',
              'StartCom Class 2 Primary Intermediate Server CA'),)),
 'notAfter': 'Nov 22 08:15:19 2013 GMT',
 'notBefore': 'Nov 21 03:09:52 2011 GMT',
 'serialNumber': '95F0',
 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),),
             (('countryName', 'US'),),
             (('stateOrProvinceName', 'California'),),
             (('localityName', 'San Francisco'),),
             (('organizationName', 'Electronic Frontier Foundation, Inc.'),),
             (('commonName', '*.eff.org'),),
             (('emailAddress', 'hostmaster@eff.org'),)),
 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')),
 'version': 3}

如果 binary_form 引數為 True,並且提供了證書,則此方法返回整個證書的 DER 編碼形式作為位元組序列,或者如果對端未提供證書,則返回 None。對端是否提供證書取決於 SSL 套接字的角色:

  • 對於客戶端 SSL 套接字,伺服器始終提供證書,無論是否需要驗證;

  • 對於伺服器 SSL 套接字,客戶端僅在伺服器請求時提供證書;因此,如果您使用了 CERT_NONE(而不是 CERT_OPTIONALCERT_REQUIRED),getpeercert() 將返回 None

另請參閱 SSLContext.check_hostname

版本 3.2 已更改: 返回的字典包含其他項,例如 issuernotBefore

版本 3.4 已更改: 當握手未完成時,會引發 ValueError。返回的字典包含其他 X509v3 擴充套件項,例如 crlDistributionPointscaIssuersOCSP URI。

版本 3.9 已更改: IPv6 地址字串不再有尾隨換行符。

SSLSocket.get_verified_chain()

以 DER 編碼的位元組列表的形式返回 SSL 通道另一端提供的已驗證證書鏈。如果停用了證書驗證,則該方法與 get_unverified_chain() 的行為相同。

在 3.13 版本加入。

SSLSocket.get_unverified_chain()

以 DER 編碼的位元組列表的形式返回 SSL 通道另一端提供的原始證書鏈。

在 3.13 版本加入。

SSLSocket.cipher()

返回一個三元素元組,包含正在使用的密碼套件的名稱、定義其使用的 SSL 協議版本以及使用的金鑰位數。如果尚未建立連線,則返回 None

SSLSocket.shared_ciphers()

返回客戶端和伺服器都可用的密碼套件列表。返回列表的每個條目都是一個三元素元組,包含密碼套件的名稱、定義其使用的 SSL 協議版本以及密碼套件使用的金鑰位數。如果尚未建立連線或套接字是客戶端套接字,則 shared_ciphers() 返回 None

在 3.5 版本加入。

SSLSocket.compression()

返回使用的壓縮演算法(作為字串),或者如果連線未壓縮,則返回 None

如果更高階的協議支援自己的壓縮機制,您可以使用 OP_NO_COMPRESSION 來停用 SSL 級別的壓縮。

在 3.3 版本加入。

SSLSocket.get_channel_binding(cb_type='tls-unique')

獲取當前連線的通道繫結資料,作為位元組物件。如果未連線或握手尚未完成,則返回 None

cb_type 引數允許選擇所需的通道繫結型別。有效的通道繫結型別列在 CHANNEL_BINDING_TYPES 列表中。目前僅支援 RFC 5929 中定義的“tls-unique”通道繫結。如果請求不支援的通道繫結型別,將引發 ValueError

在 3.3 版本加入。

SSLSocket.selected_alpn_protocol()

返回 TLS 握手期間選擇的協議。如果未呼叫 SSLContext.set_alpn_protocols(),或者對端不支援 ALPN,或者此套接字不支援任何客戶端提出的協議,或者尚未發生握手,則返回 None

在 3.5 版本加入。

SSLSocket.selected_npn_protocol()

返回 TLS/SSL 握手期間選擇的高層協議。如果未呼叫 SSLContext.set_npn_protocols(),或者對端不支援 NPN,或者握手尚未發生,則返回 None

在 3.3 版本加入。

版本 3.10 已棄用: NPN 已被 ALPN 取代。

SSLSocket.unwrap()

執行 SSL 關閉握手,該握手會從底層套接字移除 TLS 層,並返回底層套接字物件。這可用於從加密操作轉向未加密操作。應始終使用返回的套接字與連線的另一端進行進一步通訊,而不是原始套接字。

SSLSocket.verify_client_post_handshake()

請求 TLS 1.3 客戶端進行握手後身份驗證 (PHA)。PHA 只能從伺服器端套接字針對 TLS 1.3 連線發起,在初始 TLS 握手之後,並且雙方都啟用了 PHA,請參見 SSLContext.post_handshake_auth

該方法不會立即執行證書交換。伺服器端將在下一個寫入事件期間傳送 CertificateRequest,並期望客戶端在下一個讀取事件中用證書進行響應。

如果任何先決條件不滿足(例如,不是 TLS 1.3、未啟用 PHA),則會引發 SSLError

備註

僅在使用 OpenSSL 1.1.1 和啟用了 TLS 1.3 時可用。如果不支援 TLS 1.3,則該方法將引發 NotImplementedError

在 3.8 版本加入。

SSLSocket.version()

返回連線協商的實際 SSL 協議版本(作為字串),或者在沒有建立安全連線時返回 None。截至目前,可能返回值包括 "SSLv2""SSLv3""TLSv1""TLSv1.1""TLSv1.2"。較新的 OpenSSL 版本可能定義更多返回值。

在 3.5 版本加入。

SSLSocket.pending()

返回已解密並可供讀取的位元組數,這些位元組在連線中等待。

SSLSocket.context

此 SSL 套接字關聯的 SSLContext 物件。

在 3.2 版本加入。

SSLSocket.server_side

一個布林值,對於伺服器端套接字為 True,對於客戶端套接字為 False

在 3.2 版本加入。

SSLSocket.server_hostname

伺服器的主機名:str 型別,或者對於伺服器端套接字為 None,或者如果在建構函式中未指定主機名。

在 3.2 版本加入。

版本 3.7 已更改: 該屬性現在始終是 ASCII 文字。當 server_hostname 是國際化域名 (IDN) 時,此屬性現在儲存 A-label 形式("xn--pythn-mua.org"),而不是 U-label 形式("pythön.org")。

SSLSocket.session

此 SSL 連線的 SSLSession。在 TLS 握手完成後,會話對於客戶端和伺服器端套接字均可用。對於客戶端套接字,可以在呼叫 do_handshake() 之前設定會話以重用會話。

在 3.6 版本加入。

SSLSocket.session_reused

在 3.6 版本加入。

SSL 上下文

在 3.2 版本加入。

SSL 上下文儲存比單個 SSL 連線更長期的各種資料,例如 SSL 配置選項、證書和私鑰。它還管理伺服器端套接字的 SSL 會話快取,以加快同一客戶端的重複連線速度。

class ssl.SSLContext(protocol=None)

建立一個新的 SSL 上下文。您可以傳遞 protocol,它必須是此模組中定義的 PROTOCOL_* 常量之一。該引數指定要使用的 SSL 協議版本。通常,伺服器選擇特定的協議版本,客戶端必須適應伺服器的選擇。大多數版本與其他版本不相容。如果未指定,則預設為 PROTOCOL_TLS;它提供了與其他版本最大的相容性。

下表顯示了客戶端(縱向)的哪些版本可以連線到伺服器(橫向)的哪些版本:

客戶端 / **伺服器**

SSLv2

SSLv3

**TLS** [3]

TLSv1

TLSv1.1

TLSv1.2

SSLv2

[1]

SSLv3

[2]

TLS (SSLv23) [3]

[1]

[2]

TLSv1

TLSv1.1

TLSv1.2

腳註

參見

create_default_context()ssl 模組為給定的目的選擇安全設定。

版本 3.6 已更改: 上下文以安全預設值建立。預設設定了選項 OP_NO_COMPRESSIONOP_CIPHER_SERVER_PREFERENCEOP_SINGLE_DH_USEOP_SINGLE_ECDH_USEOP_NO_SSLv2OP_NO_SSLv3(除了 PROTOCOL_SSLv3)。初始密碼套件列表僅包含 HIGH 密碼,不包含 NULL 密碼和 MD5 密碼。

版本 3.10 已棄用: 不帶協議引數的 SSLContext 已棄用。將來,上下文類將需要 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 協議。

版本 3.10 已更改: 預設密碼套件現在僅包括具有前向保密和安全級別 2 的安全 AES 和 ChaCha20 密碼。禁止使用少於 2048 位的 RSA 和 DH 金鑰,以及少於 224 位的 ECC 金鑰。PROTOCOL_TLSPROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 使用 TLS 1.2 作為最低 TLS 版本。

備註

SSLContext 在被連線使用後僅支援有限的變異。允許向內部信任儲存新增新證書,但更改密碼套件、驗證設定或 mTLS 證書可能會導致意外行為。

備註

SSLContext 設計為可共享並供多個連線使用。因此,只要它在連線使用後不被重新配置,它就是執行緒安全的。

SSLContext 物件具有以下方法和屬性:

SSLContext.cert_store_stats()

獲取有關已載入的 X.509 證書數量、標記為 CA 證書的 X.509 證書數量以及證書撤銷列表(CRL)數量的統計資訊,以字典形式返回。

對於包含一個 CA 證書和另一個證書的上下文的示例:

>>> context.cert_store_stats()
{'crl': 0, 'x509_ca': 1, 'x509': 2}

在 3.4 版本加入。

SSLContext.load_cert_chain(certfile, keyfile=None, password=None)

載入私鑰和相應的證書。certfile 字串必須是 PEM 格式的單個檔案的路徑,其中包含證書以及建立證書真實性所需的任何數量的 CA 證書。keyfile 字串(如果存在)必須指向包含私鑰的檔案。否則,私鑰也將從 certfile 中獲取。有關證書如何在 certfile 中儲存的資訊,請參閱關於 證書 的討論。

password 引數可以是一個函式,在需要解密私鑰時呼叫該函式來獲取密碼。僅當私鑰已加密且需要密碼時才會呼叫它。它將不帶引數地呼叫,並且應返回一個字串、位元組或 bytearray。如果返回值是字串,則在用於解密金鑰之前將以 UTF-8 編碼。或者,可以直接將字串、位元組或 bytearray 值作為 password 引數提供。如果私鑰未加密且不需要密碼,則將忽略該引數。

如果未指定 password 引數並且需要密碼,則將使用 OpenSSL 的內建密碼提示機制與使用者互動以獲取密碼。

如果私鑰與證書不匹配,則會引發 SSLError

版本 3.3 已更改: 新增可選引數 password

SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)

從預設位置載入一組預設的“證書頒發機構”(CA)證書。在 Windows 上,它從 CAROOT 系統儲存中載入 CA 證書。在所有系統上,它會呼叫 SSLContext.set_default_verify_paths()。將來,該方法可能還會從其他位置載入 CA 證書。

purpose 標誌指定載入哪種型別的 CA 證書。預設設定 Purpose.SERVER_AUTH 載入已標記並受信任用於 TLS Web 伺服器身份驗證(客戶端套接字)的證書。Purpose.CLIENT_AUTH 載入用於伺服器端客戶端證書驗證的 CA 證書。

在 3.4 版本加入。

SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)

載入一組“證書頒發機構”(CA)證書,用於在 verify_mode 不是 CERT_NONE 時驗證其他對端證書。必須指定 cafilecapath 中的至少一個。

此方法還可以載入 PEM 或 DER 格式的證書撤銷列表(CRL)。為了使用 CRL,必須正確配置 SSLContext.verify_flags

cafile 字串(如果存在)是 PEM 格式的連線 CA 證書檔案的路徑。有關在此檔案中排列證書的資訊,請參閱關於 證書 的討論。

capath 字串(如果存在)是包含多個 PEM 格式 CA 證書的目錄的路徑,遵循 OpenSSL 特定佈局

cadata 物件(如果存在)是一個 ASCII 字串,包含一個或多個 PEM 編碼的證書,或者是一個 類位元組物件,包含 DER 編碼的證書。與 capath 類似,PEM 編碼證書周圍的多餘行將被忽略,但至少必須存在一個證書。

版本 3.4 已更改: 新增可選引數 cadata

SSLContext.get_ca_certs(binary_form=False)

獲取已載入的“證書頒發機構”(CA)證書列表。如果 binary_form 引數為 False,則列表的每個條目都是一個字典,類似於 SSLSocket.getpeercert() 的輸出。否則,該方法返回 DER 編碼證書的列表。返回的列表不包含 capath 中的證書,除非在 SSL 連線中請求並載入了某個證書。

備註

capath 目錄中的證書不會被載入,除非它們至少被使用過一次。

在 3.4 版本加入。

SSLContext.get_ciphers()

獲取已啟用密碼套件的列表。列表按優先順序排序。請參閱 SSLContext.set_ciphers()

示例

>>> ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
>>> ctx.get_ciphers()
[{'aead': True,
  'alg_bits': 256,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(256) Mac=AEAD',
  'digest': None,
  'id': 50380848,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES256-GCM-SHA384',
  'protocol': 'TLSv1.2',
  'strength_bits': 256,
  'symmetric': 'aes-256-gcm'},
 {'aead': True,
  'alg_bits': 128,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(128) Mac=AEAD',
  'digest': None,
  'id': 50380847,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES128-GCM-SHA256',
  'protocol': 'TLSv1.2',
  'strength_bits': 128,
  'symmetric': 'aes-128-gcm'}]

在 3.6 版本加入。

SSLContext.set_default_verify_paths()

從 OpenSSL 庫構建時定義的本地檔案系統路徑載入一組預設的“證書頒發機構”(CA)證書。不幸的是,沒有簡單的方法可以知道此方法是否成功:如果沒有找到證書,則不返回錯誤。當 OpenSSL 庫作為作業系統的一部分提供時,它很可能已正確配置。

SSLContext.set_ciphers(ciphers, /)

設定此上下文建立的套接字可用的密碼套件。它應該是一個遵循 OpenSSL 密碼列表格式 的字串。如果無法選擇任何密碼套件(因為編譯時選項或其他配置禁止使用所有指定的密碼套件),則會引發 SSLError

備註

連線後,SSLSocket.cipher() 方法將顯示當前選擇的密碼套件。

TLS 1.3 密碼套件不能用 set_ciphers() 停用。

SSLContext.set_alpn_protocols(alpn_protocols)

指定套接字在 SSL/TLS 握手期間應通告哪些協議。它應該是一個 ASCII 字串列表,例如 ['http/1.1', 'spdy/2'],按偏好排序。協議的選擇將在握手過程中進行,並根據 RFC 7301 進行處理。在成功握手後,SSLSocket.selected_alpn_protocol() 方法將返回商定的協議。

如果 HAS_ALPNFalse,此方法將引發 NotImplementedError

在 3.5 版本加入。

SSLContext.set_npn_protocols(npn_protocols)

指定套接字在 SSL/TLS 握手期間應通告哪些協議。它應該是一個字串列表,例如 ['http/1.1', 'spdy/2'],按偏好排序。協議的選擇將在握手過程中進行,並根據 應用層協議協商 進行處理。在成功握手後,SSLSocket.selected_npn_protocol() 方法將返回商定的協議。

如果 HAS_NPNFalse,此方法將引發 NotImplementedError

在 3.3 版本加入。

版本 3.10 已棄用: NPN 已被 ALPN 取代。

SSLContext.sni_callback

註冊一個回撥函式,當 TLS 客戶端指定伺服器名稱指示時,該函式將在 SSL/TLS 伺服器接收到 TLS Client Hello 握手訊息後被呼叫。伺服器名稱指示機制在 RFC 6066 第 3 節 - 伺服器名稱指示 中定義。

每個 SSLContext 只能設定一個回撥。如果 sni_callback 設定為 None,則停用回撥。後續呼叫此函式將停用之前註冊的回撥。

回撥函式將使用三個引數呼叫;第一個是 ssl.SSLSocket,第二個是表示客戶端意圖通訊的伺服器名稱的字串(如果 TLS Client Hello 不包含伺服器名稱,則為 None),第三個引數是原始 SSLContext。伺服器名稱引數是文字。對於國際化域名,伺服器名稱是 IDN A-label("xn--pythn-mua.org")。

此回撥的典型用法是將 ssl.SSLSocketSSLSocket.context 屬性更改為代表與伺服器名稱匹配的證書鏈的新 SSLContext 物件。

由於 TLS 連線的早期協商階段,只能使用有限的方法和屬性,如 SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert()SSLSocket.get_verified_chain()SSLSocket.get_unverified_chain()SSLSocket.cipher()SSLSocket.compression() 方法要求 TLS 連線已超越 TLS Client Hello 階段,因此不會返回有意義的值,也無法安全呼叫。

回撥函式必須返回 None 以允許 TLS 協商繼續。如果需要 TLS 失敗,可以返回常量 ALERT_DESCRIPTION_*。其他返回值將導致以 ALERT_DESCRIPTION_INTERNAL_ERROR 告警的 TLS 致命錯誤。

如果從 sni_callback 函式引發異常,TLS 連線將以 fatal TLS 告警訊息 ALERT_DESCRIPTION_HANDSHAKE_FAILURE 終止。

如果 OpenSSL 庫在構建時定義了 OPENSSL_NO_TLSEXT,則此方法將引發 NotImplementedError

在 3.7 版本加入。

SSLContext.set_servername_callback(server_name_callback)

這是一個為向後相容而保留的舊 API。儘可能時,您應該使用 sni_callback。給定的 server_name_callbacksni_callback 類似,只是當伺服器主機名是 IDN 編碼的國際化域名時,server_name_callback 接收的是解碼後的 U-label("pythön.org")。

如果伺服器名稱出現解碼錯誤,TLS 連線將以 ALERT_DESCRIPTION_INTERNAL_ERROR 致命 TLS 警報訊息終止與客戶端的連線。

在 3.4 版本加入。

SSLContext.load_dh_params(dhfile, /)

載入用於 Diffie-Hellman (DH) 金鑰交換的金鑰生成引數。使用 DH 金鑰交換以計算資源(伺服器和客戶端)為代價來提高前向保密性。dhfile 引數應該是包含 PEM 格式 DH 引數的檔案的路徑。

此設定不適用於客戶端套接字。您還可以使用 OP_SINGLE_DH_USE 選項來進一步提高安全性。

在 3.3 版本加入。

SSLContext.set_ecdh_curve(curve_name, /)

設定基於橢圓曲線的 Diffie-Hellman (ECDH) 金鑰交換的曲線名稱。ECDH 比普通 DH 快得多,同時安全性也同樣高。curve_name 引數應該是描述一個知名橢圓曲線的字串,例如 prime256v1,這是一個廣泛支援的曲線。

此設定不適用於客戶端套接字。您還可以使用 OP_SINGLE_ECDH_USE 選項來進一步提高安全性。

如果 HAS_ECDHFalse,則此方法不可用。

在 3.3 版本加入。

參見

SSL/TLS 與前向保密

Vincent Bernat。

SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=None, session=None)

包裝現有的 Python 套接字 sock,並返回一個 SSLContext.sslsocket_class(預設為 SSLSocket)的例項。返回的 SSL 套接字與上下文、其設定和證書繫結。sock 必須是 SOCK_STREAM 套接字;不支援其他套接字型別。

引數 server_side 是一個布林值,用於標識此套接字是需要伺服器端行為還是客戶端行為。

對於客戶端套接字,上下文的構建是惰性的;如果底層套接字尚未連線,則上下文構建將在呼叫套接字的 connect() 之後執行。對於伺服器端套接字,如果套接字沒有遠端對等方,則假定它是一個監聽套接字,並且伺服器端的 SSL 包裝會在透過 accept() 方法接受的客戶端連線上自動執行。該方法可能會引發 SSLError

在客戶端連線上,可選引數 server_hostname 指定我們正在連線的服務的主機名。這允許單個伺服器託管多個基於 SSL 的服務,並具有不同的證書,這與 HTTP 虛擬主機非常相似。指定 server_hostname 如果 server_side 為 true,則會引發 ValueError

引數 do_handshake_on_connect 指定是自動執行 SSL 握手(在執行 socket.connect() 之後),還是由應用程式顯式呼叫,透過呼叫 SSLSocket.do_handshake() 方法。顯式呼叫 SSLSocket.do_handshake() 使程式能夠控制握手中涉及的套接字 I/O 的阻塞行為。

引數 suppress_ragged_eofs 指定 SSLSocket.recv() 方法應如何處理來自連線另一端的意外 EOF。如果指定為 True(預設值),則響應底層套接字引發的意外 EOF 錯誤,它將返回一個正常的 EOF(空位元組物件);如果指定為 False,它將把異常拋回給呼叫者。

session,參見 session

要將 SSLSocket 包裝在另一個 SSLSocket 中,請使用 SSLContext.wrap_bio()

3.5 版更改:始終允許傳遞 server_hostname,即使 OpenSSL 沒有 SNI。

3.6 版更改:添加了 session 引數。

3.7 版更改:該方法返回 SSLContext.sslsocket_class 的例項,而不是硬編碼的 SSLSocket

SSLContext.sslsocket_class

SSLContext.wrap_socket() 的返回型別,預設為 SSLSocket。該屬性可以賦值給 SSLContext 的例項,以返回 SSLSocket 的自定義子類。

在 3.7 版本加入。

SSLContext.wrap_bio(incoming, outgoing, server_side=False, server_hostname=None, session=None)

包裝 BIO 物件 incomingoutgoing,並返回一個 SSLContext.sslobject_class(預設為 SSLObject)的例項。SSL 例程將從 incoming BIO 讀取輸入資料,並向 outgoing BIO 寫入資料。

server_sideserver_hostnamesession 引數的含義與 SSLContext.wrap_socket() 中的相同。

3.6 版更改:添加了 session 引數。

3.7 版更改:該方法返回 SSLContext.sslobject_class 的例項,而不是硬編碼的 SSLObject

SSLContext.sslobject_class

SSLContext.wrap_bio() 的返回型別,預設為 SSLObject。可以覆蓋該屬性以返回 SSLObject 的自定義子類。

在 3.7 版本加入。

SSLContext.session_stats()

獲取有關此上下文建立或管理的 SSL 會話的統計資訊。返回一個字典,其中將每個資訊片段的名稱對映到其數值。例如,以下是自建立上下文以來會話快取中的命中和未命中總數

>>> stats = context.session_stats()
>>> stats['hits'], stats['misses']
(0, 0)
SSLContext.check_hostname

是否在 SSLSocket.do_handshake() 中匹配對等證書的主機名。上下文的 verify_mode 必須設定為 CERT_OPTIONALCERT_REQUIRED,並且您必須將 server_hostname 傳遞給 wrap_socket() 以匹配主機名。啟用主機名檢查會自動將 verify_modeCERT_NONE 設定為 CERT_REQUIRED。只要啟用了主機名檢查,就不能將其設定回 CERT_NONE。協議 PROTOCOL_TLS_CLIENT 預設啟用主機名檢查。對於其他協議,必須顯式啟用主機名檢查。

示例

import socket, ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
context.load_default_certs()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com')
ssl_sock.connect(('www.verisign.com', 443))

在 3.4 版本加入。

3.7 版更改:啟用主機名檢查時,如果 verify_modeCERT_NONE,則 verify_mode 現在會自動更改為 CERT_REQUIRED。以前,同樣的操作會因 ValueError 而失敗。

SSLContext.keylog_filename

每當生成或接收金鑰材料時,將 TLS 金鑰寫入金鑰日誌檔案。金鑰日誌檔案僅用於除錯目的。檔案格式由 NSS 指定,並被 Wireshark 等許多流量分析器使用。日誌檔案以追加模式開啟。寫入線上程之間同步,但在程序之間不進行同步。

在 3.8 版本加入。

SSLContext.maximum_version

TLSVersion 列舉成員,表示支援的最高 TLS 版本。該值預設為 TLSVersion.MAXIMUM_SUPPORTED。對於 PROTOCOL_TLSPROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 以外的協議,此屬性是隻讀的。

屬性 maximum_versionminimum_versionSSLContext.options 都會影響上下文支援的 SSL 和 TLS 版本。該實現不會阻止無效的組合。例如,一個在 options 中包含 OP_NO_TLSv1_2maximum_version 設定為 TLSVersion.TLSv1_2 的上下文將無法建立 TLS 1.2 連線。

在 3.7 版本加入。

SSLContext.minimum_version

SSLContext.maximum_version 類似,但它是最低支援版本或 TLSVersion.MINIMUM_SUPPORTED

在 3.7 版本加入。

SSLContext.num_tickets

控制 PROTOCOL_TLS_SERVER 上下文的 TLS 1.3 會話票證的數量。此設定對 TLS 1.0 到 1.2 的連線沒有影響。

在 3.8 版本加入。

SSLContext.options

一個整數,表示在此上下文中啟用的 SSL 選項集。預設值為 OP_ALL,但您可以透過 OR 操作來指定其他選項,例如 OP_NO_SSLv2

3.6 版更改:SSLContext.options 返回 Options 標誌

>>> ssl.create_default_context().options
<Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_NO_COMPRESSION: 2197947391>

3.7 版已棄用:所有 OP_NO_SSL*OP_NO_TLS* 選項自 Python 3.7 起已棄用。請改用 SSLContext.minimum_versionSSLContext.maximum_version

SSLContext.post_handshake_auth

啟用 TLS 1.3 握手後客戶端身份驗證。握手後身份驗證預設停用,伺服器只能在初始握手中請求 TLS 客戶端證書。啟用後,伺服器可以在握手後的任何時間請求 TLS 客戶端證書。

在客戶端套接字上啟用時,客戶端向伺服器發出訊號,表明它支援握手後身份驗證。

在伺服器端套接字上啟用時,SSLContext.verify_mode 也必須設定為 CERT_OPTIONALCERT_REQUIRED。實際的客戶端證書交換將延遲到呼叫 SSLSocket.verify_client_post_handshake() 並執行一些 I/O 操作之後。

在 3.8 版本加入。

SSLContext.protocol

在構造上下文時選擇的協議版本。此屬性是隻讀的。

SSLContext.hostname_checks_common_name

在缺少主題備用名稱擴充套件時,check_hostname 是否回退以驗證證書的主題通用名稱(預設值:true)。

在 3.7 版本加入。

3.10 版更改:在 OpenSSL 1.1.1l 之前的版本中,該標誌無效。Python 3.8.9、3.9.3 和 3.10 包含了對先前版本的解決方法。

SSLContext.security_level

一個整數,表示上下文的安全級別。此屬性是隻讀的。

在 3.10 版本加入。

SSLContext.verify_flags

證書驗證操作的標誌。您可以透過 OR 操作來設定標誌,例如 VERIFY_CRL_CHECK_LEAF。預設情況下,OpenSSL 不要求也不驗證證書撤銷列表 (CRL)。

在 3.4 版本加入。

3.6 版更改:SSLContext.verify_flags 返回 VerifyFlags 標誌

>>> ssl.create_default_context().verify_flags
<VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
SSLContext.verify_mode

是否嘗試驗證其他對等方的證書以及在驗證失敗時如何處理。此屬性必須是 CERT_NONECERT_OPTIONALCERT_REQUIRED 之一。

3.6 版更改:SSLContext.verify_mode 現在返回 VerifyMode 列舉

>>> ssl.create_default_context().verify_mode
<VerifyMode.CERT_REQUIRED: 2>
SSLContext.set_psk_client_callback(callback)

在客戶端連線上啟用 TLS-PSK(預共享金鑰)身份驗證。

總的來說,應優先使用基於證書的身份驗證,而不是此方法。

引數 callback 是一個可呼叫物件,其簽名如下:def callback(hint: str | None) -> tuple[str | None, bytes]hint 引數是伺服器傳送的可選身份提示。返回值是一個(client-identity, psk)形式的元組。Client-identity 是一個可選字串,可用於伺服器選擇相應的 PSK。UTF-8 編碼後,該字串的最大長度為 256 個位元組。PSK 是一個表示預共享金鑰的類位元組物件。返回一個零長度的 PSK 以拒絕連線。

callback 設定為 None 會移除任何現有的回撥。

備註

使用 TLS 1.3 時

  • hint 引數始終為 None

  • client-identity 必須是非空字串。

用法示例:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
context.maximum_version = ssl.TLSVersion.TLSv1_2
context.set_ciphers('PSK')

# A simple lambda:
psk = bytes.fromhex('c0ffee')
context.set_psk_client_callback(lambda hint: (None, psk))

# A table using the hint from the server:
psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'),
              'ServerId_2': bytes.fromhex('facade')
}
def callback(hint):
    return 'ClientId_1', psk_table.get(hint, b'')
context.set_psk_client_callback(callback)

如果 HAS_PSKFalse,此方法將引發 NotImplementedError

在 3.13 版本加入。

SSLContext.set_psk_server_callback(callback, identity_hint=None)

在伺服器端連線上啟用 TLS-PSK(預共享金鑰)身份驗證。

總的來說,應優先使用基於證書的身份驗證,而不是此方法。

引數 callback 是一個可呼叫物件,其簽名如下:def callback(identity: str | None) -> bytesidentity 引數是客戶端傳送的可選身份,可用於選擇相應的 PSK。返回值是一個表示預共享金鑰的類位元組物件。返回一個零長度的 PSK 以拒絕連線。

callback 設定為 None 會移除任何現有的回撥。

引數 identity_hint 是一個可選的身份提示字串,傳送給客戶端。UTF-8 編碼後,該字串的最大長度為 256 個位元組。

備註

使用 TLS 1.3 時,identity_hint 引數不會發送給客戶端。

用法示例:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.maximum_version = ssl.TLSVersion.TLSv1_2
context.set_ciphers('PSK')

# A simple lambda:
psk = bytes.fromhex('c0ffee')
context.set_psk_server_callback(lambda identity: psk)

# A table using the identity of the client:
psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'),
              'ClientId_2': bytes.fromhex('facade')
}
def callback(identity):
    return psk_table.get(identity, b'')
context.set_psk_server_callback(callback, 'ServerId_1')

如果 HAS_PSKFalse,此方法將引發 NotImplementedError

在 3.13 版本加入。

證書

一般而言,證書是公鑰/私鑰系統的一部分。在此係統中,每個主體(可以是機器、個人或組織)都被分配一個唯一的兩部分加密金鑰。金鑰的一部分是公開的,稱為公鑰;另一部分是保密的,稱為私鑰。這兩部分是相關的,因為如果你用其中一部分加密訊息,你可以用另一部分解密它,而且**只有**用另一部分才能解密。

證書包含關於兩個主體的 {0}。它包含一個*主體*的名稱及其公鑰。它還包含第二個主體*頒發者*的宣告,即主體就是其聲稱的樣子,並且這是主體的公鑰。頒發者的宣告是用頒發者的私鑰簽名的,而這個私鑰只有頒發者知道。然而,任何人都可以透過查詢頒發者的公鑰、用其解密宣告並與證書中的其他資訊進行比較來驗證頒發者的宣告。證書還包含關於其有效時間段的資訊。這表示為兩個欄位,稱為“notBefore”和“notAfter”。

在 Python 使用證書時,客戶端或伺服器可以使用證書來證明其身份。網路連線的另一方也可以被要求提供證書,並且該證書可以被驗證以使要求此類驗證的客戶端或伺服器滿意。連線嘗試可以設定為在驗證失敗時引發異常。驗證由底層 OpenSSL 框架自動完成;應用程式不必關心其內部機制。但是,應用程式通常需要提供證書集以允許此過程進行。

Python 使用檔案來包含證書。它們應格式化為“PEM”(參見 RFC 1422),這是一種 base-64 編碼形式,用標題行和頁尾行包裝

-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----

證書鏈

包含證書的 Python 檔案可以包含一系列證書,有時稱為*證書鏈*。此鏈應以特定證書開始,該證書是“是”客戶端或伺服器的主體,然後是該證書的頒發者的證書,然後是*該*證書的頒發者的證書,依此類推,直到獲得一個*自簽名*的證書,也就是說,一個主體和頒發者相同的證書,有時稱為*根證書*。證書應簡單地連線在一起。例如,假設我們有一個三證書鏈,從我們的伺服器證書到簽發我們伺服器證書的認證機構的證書,再到簽發認證機構證書的機構的根證書

-----BEGIN CERTIFICATE-----
... (certificate for your server)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the certificate for the CA)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the root certificate for the CA's issuer)...
-----END CERTIFICATE-----

CA 證書

如果您需要驗證連線另一方的證書,您需要提供一個“CA 證書”檔案,其中包含您願意信任的每個頒發者的證書鏈。同樣,此檔案只是將這些鏈串聯在一起。對於驗證,Python 將使用它在檔案中找到的第一個匹配的鏈。可以透過呼叫 SSLContext.load_default_certs() 來使用平臺的證書檔案,這透過 create_default_context() 自動完成。

組合金鑰和證書

通常,私鑰與證書儲存在同一檔案中;在這種情況下,只需要傳遞 SSLContext.load_cert_chain() 的 `certfile` 引數。如果私鑰與證書一起儲存,它應位於證書鏈中的第一個證書之前

-----BEGIN RSA PRIVATE KEY-----
... (private key in base64 encoding) ...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----

自簽名證書

如果您要建立一個提供 SSL 加密連線服務的伺服器,您將需要為該服務獲取一個證書。獲取合適的證書有許多方法,例如從證書頒發機構購買。另一種常見的做法是生成一個自簽名證書。最簡單的方法是使用 OpenSSL 包,使用類似以下內容的方法

% openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem
Generating a 1024 bit RSA private key
.......++++++
.............................++++++
writing new private key to 'cert.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:MyState
Locality Name (eg, city) []:Some City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc.
Organizational Unit Name (eg, section) []:My Group
Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com
Email Address []:ops@myserver.mygroup.myorganization.com
%

自簽名證書的缺點是它是自己的根證書,而且其他任何人都不會在他們已知的(和受信任的)根證書快取中找到它。

示例

測試 SSL 支援

要測試 Python 安裝中是否存在 SSL 支援,使用者程式碼應使用以下習慣用法

try:
    import ssl
except ImportError:
    pass
else:
    ...  # do something that requires SSL support

客戶端操作

此示例建立一個 SSL 上下文,其中包含客戶端套接字推薦的安全設定,包括自動證書驗證

>>> context = ssl.create_default_context()

如果您更喜歡自己調整安全設定,您可以從頭開始建立一個上下文(但請注意,您可能無法獲得正確的設定)

>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")

(此程式碼段假定您的作業系統將所有 CA 證書的包放在 /etc/ssl/certs/ca-bundle.crt 中;如果不是,您將收到錯誤並且需要調整位置)

協議 PROTOCOL_TLS_CLIENT 配置上下文以進行證書驗證和主機名驗證。verify_mode 設定為 CERT_REQUIREDcheck_hostname 設定為 True。所有其他協議建立的 SSL 上下文均具有不安全的預設值。

當您使用上下文連線到伺服器時,CERT_REQUIREDcheck_hostname 會驗證伺服器證書:它確保伺服器證書是用 CA 證書之一簽名的,檢查簽名是否正確,並驗證諸如有效性和主機名標識等其他屬性

>>> conn = context.wrap_socket(socket.socket(socket.AF_INET),
...                            server_hostname="www.python.org")
>>> conn.connect(("www.python.org", 443))

然後您可以獲取證書

>>> cert = conn.getpeercert()

目視檢查表明證書確實標識了所需的服務(即,HTTPS 主機 www.python.org

>>> pprint.pprint(cert)
{'OCSP': ('http://ocsp.digicert.com',),
 'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt',),
 'crlDistributionPoints': ('http://crl3.digicert.com/sha2-ev-server-g1.crl',
                           'http://crl4.digicert.com/sha2-ev-server-g1.crl'),
 'issuer': ((('countryName', 'US'),),
            (('organizationName', 'DigiCert Inc'),),
            (('organizationalUnitName', 'www.digicert.com'),),
            (('commonName', 'DigiCert SHA2 Extended Validation Server CA'),)),
 'notAfter': 'Sep  9 12:00:00 2016 GMT',
 'notBefore': 'Sep  5 00:00:00 2014 GMT',
 'serialNumber': '01BB6F00122B177F36CAB49CEA8B6B26',
 'subject': ((('businessCategory', 'Private Organization'),),
             (('1.3.6.1.4.1.311.60.2.1.3', 'US'),),
             (('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),),
             (('serialNumber', '3359300'),),
             (('streetAddress', '16 Allen Rd'),),
             (('postalCode', '03894-4801'),),
             (('countryName', 'US'),),
             (('stateOrProvinceName', 'NH'),),
             (('localityName', 'Wolfeboro'),),
             (('organizationName', 'Python Software Foundation'),),
             (('commonName', 'www.python.org'),)),
 'subjectAltName': (('DNS', 'www.python.org'),
                    ('DNS', 'python.org'),
                    ('DNS', 'pypi.org'),
                    ('DNS', 'docs.python.org'),
                    ('DNS', 'testpypi.org'),
                    ('DNS', 'bugs.python.org'),
                    ('DNS', 'wiki.python.org'),
                    ('DNS', 'hg.python.org'),
                    ('DNS', 'mail.python.org'),
                    ('DNS', 'packaging.python.org'),
                    ('DNS', 'pythonhosted.org'),
                    ('DNS', 'www.pythonhosted.org'),
                    ('DNS', 'test.pythonhosted.org'),
                    ('DNS', 'us.pycon.org'),
                    ('DNS', 'id.python.org')),
 'version': 3}

現在 SSL 通道已建立並證書已驗證,您可以繼續與伺服器通訊

>>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
>>> pprint.pprint(conn.recv(1024).split(b"\r\n"))
[b'HTTP/1.1 200 OK',
 b'Date: Sat, 18 Oct 2014 18:27:20 GMT',
 b'Server: nginx',
 b'Content-Type: text/html; charset=utf-8',
 b'X-Frame-Options: SAMEORIGIN',
 b'Content-Length: 45679',
 b'Accept-Ranges: bytes',
 b'Via: 1.1 varnish',
 b'Age: 2188',
 b'X-Served-By: cache-lcy1134-LCY',
 b'X-Cache: HIT',
 b'X-Cache-Hits: 11',
 b'Vary: Cookie',
 b'Strict-Transport-Security: max-age=63072000; includeSubDomains',
 b'Connection: close',
 b'',
 b'']

請參閱下文 安全注意事項 的討論。

伺服器端操作

對於伺服器操作,通常您需要一個伺服器證書和一個私鑰,每個都儲存在一個檔案中。您將首先建立一個包含金鑰和證書的上下文,以便客戶端可以檢查您的身份。然後您將開啟一個套接字,將其繫結到一個埠,在其上呼叫 listen(),並開始等待客戶端連線

import socket, ssl

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")

bindsocket = socket.socket()
bindsocket.bind(('myaddr.example.com', 10023))
bindsocket.listen(5)

當客戶端連線時,您將呼叫套接字上的 accept() 來獲取來自另一端的套接字,並使用上下文的 SSLContext.wrap_socket() 方法為連線建立一個伺服器端 SSL 套接字

while True:
    newsocket, fromaddr = bindsocket.accept()
    connstream = context.wrap_socket(newsocket, server_side=True)
    try:
        deal_with_client(connstream)
    finally:
        connstream.shutdown(socket.SHUT_RDWR)
        connstream.close()

然後您將從 connstream 讀取資料並對其進行處理,直到您完成與客戶端的通訊(或客戶端完成與您的通訊)

def deal_with_client(connstream):
    data = connstream.recv(1024)
    # empty data means the client is finished with us
    while data:
        if not do_something(connstream, data):
            # we'll assume do_something returns False
            # when we're finished with client
            break
        data = connstream.recv(1024)
    # finished with client

然後返回監聽新的客戶端連線(當然,一個真正的伺服器可能會在單獨的執行緒中處理每個客戶端連線,或者將套接字置於非阻塞模式並使用事件迴圈)。

關於非阻塞套接字

非阻塞模式下的 SSL 套接字行為與常規套接字略有不同。因此,在使用非阻塞套接字時,有幾件事需要注意

  • 大多數 SSLSocket 方法將引發 SSLWantWriteErrorSSLWantReadError,而不是 BlockingIOError,如果 I/O 操作會阻塞。如果底層套接字上的讀取操作是必需的,則會引發 SSLWantReadError,對於底層套接字上的寫入操作,則會引發 SSLWantWriteError。請注意,嘗試*寫入* SSL 套接字可能需要先*從*底層套接字讀取,而嘗試*從* SSL 套接字讀取可能需要先*寫入*底層套接字。

    3.5 版更改:在早期 Python 版本中,SSLSocket.send() 方法返回零,而不是引發 SSLWantWriteErrorSSLWantReadError

  • 呼叫 select() 告訴您作業系統級別的套接字可以讀取(或寫入),但這並不意味著上層 SSL 層有足夠的資料。例如,SSL 幀可能只收到一部分。因此,您必須準備好處理 SSLSocket.recv()SSLSocket.send() 失敗,並在再次呼叫 select() 後重試。

  • 反之,由於 SSL 層有自己的幀處理,SSL 套接字可能仍有可供讀取的資料,而 select() 並不知道。因此,您應該先呼叫 SSLSocket.recv() 來清空任何可能存在的資料,然後如果仍然需要,再阻塞於 select() 呼叫。

    (當然,當使用 poll()selectors 模組中的原語時,也適用類似的規定)

  • SSL 握手本身將是非阻塞的:必須重試 SSLSocket.do_handshake() 方法,直到它成功返回。以下是使用 select() 等待套接字就緒的概要

    while True:
        try:
            sock.do_handshake()
            break
        except ssl.SSLWantReadError:
            select.select([sock], [], [])
        except ssl.SSLWantWriteError:
            select.select([], [sock], [])
    

參見

asyncio 模組支援非阻塞 SSL 套接字,並提供更高級別的流 API。它使用 selectors 模組輪詢事件,並處理 SSLWantWriteErrorSSLWantReadErrorBlockingIOError 異常。它還會非同步執行 SSL 握手。

記憶體 BIO 支援

在 3.5 版本加入。

自從 Python 2.6 引入 SSL 模組以來,SSLSocket 類提供了兩個相關但不同的功能區域

  • SSL 協議處理

  • 網路 IO

網路 IO API 與 socket.socket 提供的 API 相同,SSLSocket 也繼承自它。這允許 SSL 套接字作為常規套接字的即插即用替換,從而非常容易地將 SSL 支援新增到現有應用程式。

將 SSL 協議處理和網路 IO 結合通常效果很好,但在某些情況下不適合。例如,非同步 IO 框架希望使用不同於 socket.socket 和內部 OpenSSL 套接字 IO 例程所假設的“基於檔案描述符的輪詢/選擇”(就緒性基礎)模型。這主要與 Windows 等平臺相關,因為該模型效率不高。為此,提供了一個 SSLSocket 的簡化範圍變體,稱為 SSLObject

class ssl.SSLObject

SSLSocket 的簡化範圍變體,表示一個不包含任何網路 IO 方法的 SSL 協議例項。該類通常由框架作者使用,他們希望透過記憶體緩衝區實現 SSL 的非同步 IO。

此類實現了 OpenSSL 實現的低階 SSL 物件之上的介面。此物件捕獲 SSL 連線的狀態,但本身不提供任何網路 IO。IO 需要透過單獨的“BIO”物件進行,而 BIO 是 OpenSSL 的 IO 抽象層。

此類沒有公共建構函式。必須使用 wrap_bio() 方法建立 SSLObject 例項。此方法將建立 SSLObject 例項並將其繫結到一對 BIO。*incoming* BIO 用於將資料從 Python 傳遞給 SSL 協議例項,而 *outgoing* BIO 用於將資料反向傳遞。

以下方法可用

SSLSocket 相比,此物件缺少以下功能

  • 任何形式的網路 IO;recv()send() 只讀寫底層 MemoryBIO 緩衝區。

  • 沒有 do_handshake_on_connect 機制。您必須始終手動呼叫 do_handshake() 來啟動握手。

  • 沒有處理 suppress_ragged_eofs。所有違反協議的 EOF 條件都透過 SSLEOFError 異常報告。

  • 方法 unwrap() 的呼叫不返回任何內容,而對於 SSL 套接字,它會返回底層套接字。

  • SSLContext.set_servername_callback() 傳遞給 server_name_callback 回撥將獲得一個 SSLObject 例項,而不是作為其第一個引數的 SSLSocket 例項。

一些與使用 SSLObject 相關的註釋

3.7 版更改:SSLObject 例項必須使用 wrap_bio() 建立。在早期版本中,可以直接建立例項。這從未被記錄或正式支援。

SSLObject 透過記憶體緩衝區與外部世界進行通訊。類 MemoryBIO 提供了一個可用於此目的的記憶體緩衝區。它包裝了一個 OpenSSL 記憶體 BIO(基本 IO)物件

class ssl.MemoryBIO

一個記憶體緩衝區,可用於在 Python 和 SSL 協議例項之間傳遞資料。

pending

返回當前在記憶體緩衝區中的位元組數。

eof

一個布林值,指示記憶體 BIO 當前是否處於檔案末尾位置。

read(n=-1, /)

從記憶體緩衝區讀取最多 n 個位元組。如果未指定 nn 為負數,則返回所有位元組。

write(buf, /)

buf 中的位元組寫入記憶體 BIO。buf 引數必須是支援緩衝區協議的物件。

返回值是寫入的位元組數,該位元組數始終等於 buf 的長度。

write_eof()

向記憶體 BIO 寫入 EOF 標記。呼叫此方法後,呼叫 write() 是非法的。在讀取完緩衝區中當前所有資料後,屬性 eof 將變為 true。

SSL 會話

在 3.6 版本加入。

class ssl.SSLSession

session 使用的會話物件。

id
time
timeout
ticket_lifetime_hint
has_ticket

安全考慮

最佳預設值

對於**客戶端使用**,如果您對安全策略沒有特殊要求,強烈建議您使用 create_default_context() 函式來建立您的 SSL 上下文。它將載入系統的受信任 CA 證書,啟用證書驗證和主機名檢查,並嘗試選擇合理安全協議和密碼套件設定。

例如,您可以使用 smtplib.SMTP 類建立到 SMTP 伺服器的可信、安全連線

>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')

如果連線需要客戶端證書,可以使用 SSLContext.load_cert_chain() 新增。

相比之下,如果您直接呼叫 SSLContext 建構函式來建立 SSL 上下文,預設情況下它不會啟用證書驗證或主機名檢查。如果您這樣做,請閱讀以下段落以實現良好的安全級別。

手動設定

驗證證書

直接呼叫 SSLContext 建構函式時,預設值為 CERT_NONE。由於它不驗證對等方身份,因此可能不安全,尤其是在客戶端模式下,大多數情況下您會希望確保您正在通訊的伺服器的真實性。因此,在客戶端模式下,強烈建議使用 CERT_REQUIRED。但是,這本身並不足夠;您還必須檢查伺服器證書(可以透過呼叫 SSLSocket.getpeercert() 獲取)是否與所需服務匹配。對於許多協議和應用程式,服務可以透過主機名標識。當啟用 SSLContext.check_hostname 時,此常見檢查會自動執行。

3.7 版更改:主機名匹配現在由 OpenSSL 執行。Python 不再使用 match_hostname()

在伺服器模式下,如果您想透過 SSL 層驗證客戶端(而不是使用更高級別的身份驗證機制),您還必須指定 CERT_REQUIRED 並類似地檢查客戶端證書。

協議版本

SSL 版本 2 和 3 被認為是不安全的,因此使用它們很危險。如果您想在客戶端和伺服器之間實現最大相容性,建議使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 作為協議版本。SSLv2 和 SSLv3 預設停用。

>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.minimum_version = ssl.TLSVersion.TLSv1_3
>>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3

上面建立的 SSL 上下文將只允許與伺服器建立 TLSv1.3 及更高版本(如果您的系統支援)的連線。PROTOCOL_TLS_CLIENT 預設啟用證書驗證和主機名檢查。您必須將證書載入到上下文中。

密碼套件選擇

如果您有高階安全需求,可以透過 SSLContext.set_ciphers() 方法對協商 SSL 會話時啟用的密碼套件進行精細調整。從 Python 3.2.3 開始,ssl 模組預設停用某些弱密碼套件,但您可能需要進一步限制密碼套件的選擇。請務必閱讀 OpenSSL 關於密碼列表格式的文件。如果您想檢查給定密碼列表啟用了哪些密碼套件,請使用 SSLContext.get_ciphers() 或系統上的 openssl ciphers 命令。

多程序

如果將此模組用作多程序應用程式的一部分(例如,使用 multiprocessingconcurrent.futures 模組),請注意,OpenSSL 的內部隨機數生成器無法正確處理分叉的程序。如果應用程式使用 os.fork() 使用任何 SSL 功能,則必須更改父程序的 PRNG 狀態。任何成功的 RAND_add()RAND_bytes() 呼叫都已足夠。

TLS 1.3

在 3.7 版本加入。

TLS 1.3 協議的行為與之前版本的 TLS/SSL 略有不同。一些新的 TLS 1.3 功能尚不可用。

  • TLS 1.3 使用一套獨立的密碼套件。預設啟用所有 AES-GCM 和 ChaCha20 密碼套件。方法 SSLContext.set_ciphers() 尚不能啟用或停用任何 TLS 1.3 密碼,但 SSLContext.get_ciphers() 會返回它們。

  • 會話票證不再作為初始握手的一部分發送,並且處理方式也不同。 SSLSocket.sessionSSLSession 與 TLS 1.3 不相容。

  • 客戶端證書也不再在初始握手中進行驗證。伺服器可以隨時請求證書。客戶端在向伺服器傳送或接收應用程式資料時會處理證書請求。

  • TLS 1.3 的某些功能,如早期資料、延遲的 TLS 客戶端證書請求、簽名演算法配置和重新金鑰,尚不支援。