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
建構函式更高的安全級別。cafile、capath、cadata 代表用於證書驗證的可選 CA 證書,與
SSLContext.load_verify_locations()
中的 cafile 引數格式相同。如果所有三個引數都是None
,則此函式可以選擇信任系統預設的 CA 證書。設定包括:
PROTOCOL_TLS_CLIENT
或PROTOCOL_TLS_SERVER
,OP_NO_SSLv2
,以及OP_NO_SSLv3
,並帶有高加密強度的密碼套件,不含 RC4 且不含未經身份驗證的密碼套件。將SERVER_AUTH
作為 purpose 傳遞會設定verify_mode
為CERT_REQUIRED
,並載入 CA 證書(當 cafile、capath 或 cadata 中至少有一個給出時)或使用SSLContext.load_default_certs()
載入預設 CA 證書。當
keylog_filename
受支援且環境變數SSLKEYLOGFILE
已設定時,create_default_context()
會啟用金鑰記錄。此上下文的預設設定包括
VERIFY_X509_PARTIAL_CHAIN
和VERIFY_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_CLIENT
或PROTOCOL_TLS_SERVER
協議,而不是通用的PROTOCOL_TLS
。3.13 版本更改: 上下文現在在其預設驗證標誌中使用
VERIFY_X509_PARTIAL_CHAIN
和VERIFY_X509_STRICT
。
異常¶
- exception ssl.SSLError¶
當底層 SSL 實現(當前由 OpenSSL 庫提供)發生錯誤時引發。這表示疊加在底層網路連線之上的更高級別的加密和身份驗證層存在某種問題。此錯誤是
OSError
的子類。SSLError
例項的錯誤程式碼和訊息由 OpenSSL 庫提供。3.3 版本更改:
SSLError
曾是socket.error
的子類。- library¶
一個字串助記符,指示發生錯誤的 OpenSSL 子模組,例如
SSL
、PEM
或X509
。可能值的範圍取決於 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.SSLCertVerificationError¶
當證書驗證失敗時,引發
SSLError
的子類。在 3.7 版本加入。
- verify_code¶
表示驗證錯誤的數字錯誤程式碼。
- verify_message¶
表示驗證錯誤的易讀字串。
- exception ssl.CertificateError¶
-
3.7 版本更改: 此異常現在是
SSLCertVerificationError
的別名。
隨機數生成¶
- ssl.RAND_bytes(num, /)¶
返回 num 個加密安全的偽隨機位元組。如果 PRNG 未使用足夠的資料進行播種,或者當前 RAND 方法不支援該操作,則引發
SSLError
。RAND_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_version 從
PROTOCOL_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 可以是
CA
、ROOT
或MY
。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 版本加入。
常量¶
所有常量現在都是
enum.IntEnum
或enum.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_CLIENT
和PROTOCOL_TLS_SERVER
。
- ssl.PROTOCOL_TLS_CLIENT¶
自動協商客戶端和伺服器都支援的最高協議版本,併為上下文配置客戶端連線。該協議預設啟用
CERT_REQUIRED
和check_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_SERVER
或PROTOCOL_TLS_CLIENT
,並結合SSLContext.minimum_version
和SSLContext.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_version
和SSLContext.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_NEVER_CHECK_COMMON_NAME¶
OpenSSL 庫是否內建支援不檢查主題通用名稱,並且
SSLContext.hostname_checks_common_name
是可寫的。在 3.7 版本加入。
- ssl.HAS_ECDH¶
OpenSSL 庫是否內建支援基於橢圓曲線的 Diffie-Hellman 金鑰交換。除非分發者明確停用了該功能,否則這應為 true。
在 3.3 版本加入。
- 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_version
和SSLContext.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_2
和TLSVersion.TLSv1_3
之外,所有TLSVersion
成員均已棄用。
SSL 套接字¶
- class ssl.SSLSocket(socket.socket)¶
SSL 套接字提供 套接字物件 的以下方法:
recv()
,recv_into()
(但不允許傳遞非零的flags
引數)sendfile()
(但os.sendfile
將僅用於明文套接字,否則將使用send()
)
然而,由於 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_ex
和SSL_write_ex
。這些函式支援讀寫大於 2 GB 的資料。寫入零長度資料不再因協議違規錯誤而失敗。
SSL 套接字還具有以下附加方法和屬性:
- SSLSocket.read(len=1024, buffer=None)¶
從 SSL 套接字讀取最多 len 位元組的資料,並將結果作為
bytes
例項返回。如果指定了 buffer,則改用緩衝區讀取,並返回讀取的位元組數。如果套接字是 非阻塞 且讀取會阻塞,則引發
SSLWantReadError
或SSLWantWriteError
。由於隨時可能進行重新協商,呼叫
read()
也可能導致寫入操作。版本 3.5 已更改: 套接字超時不再每次接收或傳送位元組時重置。套接字超時現在是讀取最多 len 位元組的總最長時間。
版本 3.6 已棄用: 使用
recv()
而不是read()
。
- SSLSocket.write(data)¶
將 data 寫入 SSL 套接字,並返回寫入的位元組數。data 引數必須是支援緩衝區介面的物件。
如果套接字是 非阻塞 且寫入會阻塞,則引發
SSLWantReadError
或SSLWantWriteError
。由於隨時可能進行重新協商,呼叫
write()
也可能導致讀取操作。版本 3.5 已更改: 套接字超時不再每次接收或傳送位元組時重置。套接字超時現在是寫入 data 的總最長時間。
版本 3.6 已棄用: 使用
send()
而不是write()
。
備註
read()
和 write()
方法是低階方法,它們讀取和寫入未加密的應用層資料,並將其解密/加密為加密的線路上層資料。這些方法需要活動的 SSL 連線,即握手已完成且未呼叫 SSLSocket.unwrap()
。
- 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
鍵。subject
和issuer
欄位是元組,包含證書資料結構中相應欄位的相對區分名稱 (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_OPTIONAL
或CERT_REQUIRED
),getpeercert()
將返回None
。
另請參閱
SSLContext.check_hostname
。版本 3.2 已更改: 返回的字典包含其他項,例如
issuer
和notBefore
。版本 3.4 已更改: 當握手未完成時,會引發
ValueError
。返回的字典包含其他 X509v3 擴充套件項,例如crlDistributionPoints
、caIssuers
和OCSP
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
。
返回客戶端和伺服器都可用的密碼套件列表。返回列表的每個條目都是一個三元素元組,包含密碼套件的名稱、定義其使用的 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_COMPRESSION
、OP_CIPHER_SERVER_PREFERENCE
、OP_SINGLE_DH_USE
、OP_SINGLE_ECDH_USE
、OP_NO_SSLv2
和OP_NO_SSLv3
(除了PROTOCOL_SSLv3
)。初始密碼套件列表僅包含HIGH
密碼,不包含NULL
密碼和MD5
密碼。版本 3.10 已棄用: 不帶協議引數的
SSLContext
已棄用。將來,上下文類將需要PROTOCOL_TLS_CLIENT
或PROTOCOL_TLS_SERVER
協議。版本 3.10 已更改: 預設密碼套件現在僅包括具有前向保密和安全級別 2 的安全 AES 和 ChaCha20 密碼。禁止使用少於 2048 位的 RSA 和 DH 金鑰,以及少於 224 位的 ECC 金鑰。
PROTOCOL_TLS
、PROTOCOL_TLS_CLIENT
和PROTOCOL_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 上,它從
CA
和ROOT
系統儲存中載入 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
時驗證其他對端證書。必須指定 cafile 或 capath 中的至少一個。此方法還可以載入 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
。
- SSLContext.set_alpn_protocols(alpn_protocols)¶
指定套接字在 SSL/TLS 握手期間應通告哪些協議。它應該是一個 ASCII 字串列表,例如
['http/1.1', 'spdy/2']
,按偏好排序。協議的選擇將在握手過程中進行,並根據 RFC 7301 進行處理。在成功握手後,SSLSocket.selected_alpn_protocol()
方法將返回商定的協議。如果
HAS_ALPN
為False
,此方法將引發NotImplementedError
。在 3.5 版本加入。
- SSLContext.set_npn_protocols(npn_protocols)¶
指定套接字在 SSL/TLS 握手期間應通告哪些協議。它應該是一個字串列表,例如
['http/1.1', 'spdy/2']
,按偏好排序。協議的選擇將在握手過程中進行,並根據 應用層協議協商 進行處理。在成功握手後,SSLSocket.selected_npn_protocol()
方法將返回商定的協議。如果
HAS_NPN
為False
,此方法將引發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.SSLSocket
的SSLSocket.context
屬性更改為代表與伺服器名稱匹配的證書鏈的新SSLContext
物件。由於 TLS 連線的早期協商階段,只能使用有限的方法和屬性,如
SSLSocket.selected_alpn_protocol()
和SSLSocket.context
。SSLSocket.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_callback 與 sni_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_ECDH
為False
,則此方法不可用。在 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 物件 incoming 和 outgoing,並返回一個
SSLContext.sslobject_class
(預設為SSLObject
)的例項。SSL 例程將從 incoming BIO 讀取輸入資料,並向 outgoing BIO 寫入資料。server_side、server_hostname 和 session 引數的含義與
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_OPTIONAL
或CERT_REQUIRED
,並且您必須將 server_hostname 傳遞給wrap_socket()
以匹配主機名。啟用主機名檢查會自動將verify_mode
從CERT_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_mode
為CERT_NONE
,則verify_mode
現在會自動更改為CERT_REQUIRED
。以前,同樣的操作會因ValueError
而失敗。
- SSLContext.keylog_filename¶
每當生成或接收金鑰材料時,將 TLS 金鑰寫入金鑰日誌檔案。金鑰日誌檔案僅用於除錯目的。檔案格式由 NSS 指定,並被 Wireshark 等許多流量分析器使用。日誌檔案以追加模式開啟。寫入線上程之間同步,但在程序之間不進行同步。
在 3.8 版本加入。
- SSLContext.maximum_version¶
TLSVersion
列舉成員,表示支援的最高 TLS 版本。該值預設為TLSVersion.MAXIMUM_SUPPORTED
。對於PROTOCOL_TLS
、PROTOCOL_TLS_CLIENT
和PROTOCOL_TLS_SERVER
以外的協議,此屬性是隻讀的。屬性
maximum_version
、minimum_version
和SSLContext.options
都會影響上下文支援的 SSL 和 TLS 版本。該實現不會阻止無效的組合。例如,一個在options
中包含OP_NO_TLSv1_2
且maximum_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_version
和SSLContext.maximum_version
。
- SSLContext.post_handshake_auth¶
啟用 TLS 1.3 握手後客戶端身份驗證。握手後身份驗證預設停用,伺服器只能在初始握手中請求 TLS 客戶端證書。啟用後,伺服器可以在握手後的任何時間請求 TLS 客戶端證書。
在客戶端套接字上啟用時,客戶端向伺服器發出訊號,表明它支援握手後身份驗證。
在伺服器端套接字上啟用時,
SSLContext.verify_mode
也必須設定為CERT_OPTIONAL
或CERT_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.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_NONE
、CERT_OPTIONAL
或CERT_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
會移除任何現有的回撥。用法示例:
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_PSK
為False
,此方法將引發NotImplementedError
。在 3.13 版本加入。
- SSLContext.set_psk_server_callback(callback, identity_hint=None)¶
在伺服器端連線上啟用 TLS-PSK(預共享金鑰)身份驗證。
總的來說,應優先使用基於證書的身份驗證,而不是此方法。
引數
callback
是一個可呼叫物件,其簽名如下:def callback(identity: str | None) -> bytes
。identity 引數是客戶端傳送的可選身份,可用於選擇相應的 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_PSK
為False
,此方法將引發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_REQUIRED
,check_hostname 設定為 True
。所有其他協議建立的 SSL 上下文均具有不安全的預設值。
當您使用上下文連線到伺服器時,CERT_REQUIRED
和 check_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
方法將引發SSLWantWriteError
或SSLWantReadError
,而不是BlockingIOError
,如果 I/O 操作會阻塞。如果底層套接字上的讀取操作是必需的,則會引發SSLWantReadError
,對於底層套接字上的寫入操作,則會引發SSLWantWriteError
。請注意,嘗試*寫入* SSL 套接字可能需要先*從*底層套接字讀取,而嘗試*從* SSL 套接字讀取可能需要先*寫入*底層套接字。3.5 版更改:在早期 Python 版本中,
SSLSocket.send()
方法返回零,而不是引發SSLWantWriteError
或SSLWantReadError
。呼叫
select()
告訴您作業系統級別的套接字可以讀取(或寫入),但這並不意味著上層 SSL 層有足夠的資料。例如,SSL 幀可能只收到一部分。因此,您必須準備好處理SSLSocket.recv()
和SSLSocket.send()
失敗,並在再次呼叫select()
後重試。反之,由於 SSL 層有自己的幀處理,SSL 套接字可能仍有可供讀取的資料,而
select()
並不知道。因此,您應該先呼叫SSLSocket.recv()
來清空任何可能存在的資料,然後如果仍然需要,再阻塞於select()
呼叫。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
模組輪詢事件,並處理 SSLWantWriteError
、SSLWantReadError
和 BlockingIOError
異常。它還會非同步執行 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
相關的註釋SSLObject
上的所有 IO 都是非阻塞的。這意味著例如read()
會在需要比 incoming BIO 可用的資料更多時引發SSLWantReadError
。
3.7 版更改:
SSLObject
例項必須使用wrap_bio()
建立。在早期版本中,可以直接建立例項。這從未被記錄或正式支援。
SSLObject
透過記憶體緩衝區與外部世界進行通訊。類 MemoryBIO
提供了一個可用於此目的的記憶體緩衝區。它包裝了一個 OpenSSL 記憶體 BIO(基本 IO)物件
SSL 會話¶
在 3.6 版本加入。
安全考慮¶
最佳預設值¶
對於**客戶端使用**,如果您對安全策略沒有特殊要求,強烈建議您使用 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_CLIENT
或 PROTOCOL_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
命令。
多程序¶
如果將此模組用作多程序應用程式的一部分(例如,使用 multiprocessing
或 concurrent.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.session
和SSLSession
與 TLS 1.3 不相容。客戶端證書也不再在初始握手中進行驗證。伺服器可以隨時請求證書。客戶端在向伺服器傳送或接收應用程式資料時會處理證書請求。
TLS 1.3 的某些功能,如早期資料、延遲的 TLS 客戶端證書請求、簽名演算法配置和重新金鑰,尚不支援。
參見
- 類
socket.socket
底層
socket
類的文件- SSL/TLS 強加密:簡介
Apache HTTP 伺服器文件的介紹
- RFC 1422:Internet 電子郵件隱私增強:第二部分:基於證書的金鑰管理
Steve Kent
- RFC 4086:安全隨機性要求
Donald E., Jeffrey I. Schiller
- RFC 5280:Internet X.509 公鑰基礎結構證書和證書吊銷列表 (CRL) 配置檔案
D. Cooper
- RFC 5246:傳輸層安全 (TLS) 協議版本 1.2
T. Dierks 等人。
- RFC 6066:傳輸層安全 (TLS) 擴充套件
D. Eastlake
- IANA TLS:傳輸層安全 (TLS) 引數
IANA
- RFC 7525:關於安全使用傳輸層安全 (TLS) 和資料報傳輸層安全 (DTLS) 的建議
IETF
- Mozilla 的伺服器端 TLS 建議
Mozilla