ssl --- 套接字對象的TLS/SSL封裝?

源代碼: Lib/ssl.py


本模塊保證了對傳安全傳輸層協議(TLS)的訪問 (SSL)加密,并且提供客戶端和服務端層面的網絡嵌套字層面的對等連接認證技術。本模塊使用OpenSSL庫。適用于所有現代Unix系統、Windows以及Mac OS X。只要Open SSL存在的系統,都有機會正常使用。

注解

某些行為可能具有平臺依賴,因為調用是根據操作系統的嵌套字API。不同版本的Open SSL也會引起差異:例如Open SSL版本1.0.1 自帶TLSv1.1 和 TLSv1.2

警告

在閱讀 安全考量 前不要使用此模塊。 這樣做可能會導致虛假的安全感,因為ssl模塊的默認設置不一定適合你的應用程序。

本文檔記錄"ssl"模塊的對象和函數;更多關于TLS,SSL,和證書的信息,請參閱下方的“詳情”選項

本模塊提供了一個類 ssl.SSLSocket,它派生自 socket.socket 類型,并提供類似套接字的包裝器,也能夠對通過帶 SSL 套接字的數據進行加密和解密。 它支持一些額外方法例如 getpeercert(),該方法可從連接的另一端獲取證書,還有 cipher(),該方法可獲取安全連接所使用的密碼。

對于更復雜的應用程序,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。

方法、常量和異常處理?

套接字創建?

從 Python 3.2 和 2.7.9 開始,建議使用 SSLContext 實例的 SSLContext.wrap_socket() 來將套接字包裝為 SSLSocket 對象。 輔助函數 create_default_context() 會返回一個新的帶有安全默認設置的上下文。 舊的 wrap_socket() 函數已被棄用,因為它效率較差并且不支持服務器名稱提示(SNI)和主機匹配。

客戶端套接字實例,采用默認上下文和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() 的一致。 如果三個參數均為 None,此函數可以轉而選擇信任系統的默認 CA 證書。

設置為: PROTOCOL_TLS, OP_NO_SSLv2OP_NO_SSLv3,帶有不含 RC4 及未認證的高強度加密密碼套件。 傳入 SERVER_AUTH 作為 purpose 會將 verify_mode 設為 CERT_REQUIRED 并加載 CA 證書 (若給出 cafile, capathcadata 之一) 或用 SSLContext.load_default_certs() 加載默認 CA 證書。

注解

協議、選項、密碼和其他設置可隨時更改為更具約束性的值而無須事先棄用。 這些值代表了兼容性和安全性之間的合理平衡。

如果你的應用需要特定的設置,你應當創建一個 SSLContext 并自行應用設置。

注解

如果你發現當某些較舊的客戶端或服務器嘗試與用此函數創建的 SSLContext 進行連接時收到了報錯提示 "Protocol or cipher suite mismatch",這可能是因為它們只支持 SSL3.0 而它被此函數用 OP_NO_SSLv3 排除掉了。 SSL3.0 被廣泛認為 完全不可用。 如果你仍希望繼續使用此函數但仍允許 SSL 3.0 連接,你可以使用以下代碼重新啟用它們:

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

3.4 新版功能.

在 3.4.4 版更改: RC4 被從默認密碼字符串中丟棄。

在 3.6 版更改: ChaCha20/Poly1305 被添加到默認密碼字符串中。

3DES 被從默認密碼字符串中丟棄。

異常?

exception ssl.SSLError?

引發此異常以提示來自下層 SSL 實現(目前由 OpenSSL 庫提供)的錯誤。 它表示在下層網絡連接之上疊加的高層級加密和驗證層存在某種問題。 此錯誤是 OSError 的一個子類型。 SSLError 實例的錯誤和消息是由 OpenSSL 庫提供的。

在 3.3 版更改: SSLError 曾經是 socket.error 的一個子類型。

library?

一個字符串形式的助詞符,用來指明發生錯誤的 OpenSSL 子模塊,例如 SSL, PEMX509。 可能的取值范圍依賴于 OpenSSL 的版本。

3.3 新版功能.

reason?

一個字符串形式的助記符,用來指明發生錯誤的原因,例如 CERTIFICATE_VERIFY_FAILED。 可能的取值范圍依賴于 OpenSSL 的版本。

3.3 新版功能.

exception ssl.SSLZeroReturnError?

SSLError 的一個子類,當嘗試讀取或寫入且 SSL 連接已被完全關閉時會被引發。 請注意這并不意味著下層的傳輸(讀取 TCP)已被關閉。

3.3 新版功能.

exception ssl.SSLWantReadError?

SSLError 的一個子類,當嘗試讀取或寫入但,并在請求被滿足之前還需要在下層的 TCP 傳輸上接收更多數據時會被 非阻塞型 SSL 套接字 引發。

3.3 新版功能.

exception ssl.SSLWantWriteError?

SSLError 的一個子類,當嘗試讀取或寫入數據,但在請求被滿足之前還需要在下層的 TCP 傳輸上發送更多數據時會被 非阻塞型 SSL 套接字 引發。

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

Read the Wikipedia article, Cryptographically secure pseudorandom number generator (CSPRNG), to get the requirements of a cryptographically generator.

3.3 新版功能.

ssl.RAND_pseudo_bytes(num)?

返回 (bytes, is_cryptographic): bytes 是 num 個偽隨機字節數據,如果所生成的字節數據為高加密強度則 is_cryptographic 為 True。 如果當前 RAND 方法不支持此操作則會引發 SSLError

所生成的偽隨機字節序列如果具有足夠的長度則將會具有唯一性,并是并非不可預測。 它們可被用于非加密目的以及加密協議中的特定目的,但通常不可被用于密鑰生成等目的。

對于幾乎所有應用程序都更推薦使用 os.urandom()

3.3 新版功能.

3.6 版后已移除: OpenSSL 已棄用了 ssl.RAND_pseudo_bytes(),請改用 ssl.RAND_bytes()

ssl.RAND_status()?

如果 SSL 偽隨機數生成器已使用‘足夠的’隨機性作為種子則返回 True,否則返回 False。 你可以使用 ssl.RAND_egd()ssl.RAND_add() 來增加偽隨機數生成器的隨機性。

ssl.RAND_egd(path)?

如果你在某處運行了一個熵收集守護程序(EGD),且 path 是向其打開的套接字連接路徑名,此函數將從該套接字讀取 256 個字節的隨機性數據,并將其添加到 SSL 偽隨機數生成器以增加所生成密鑰的安全性。 此操作通常只在沒有更好隨機性源的系統上才是必要的。

請查看 http://egd.sourceforge.net/http://prngd.sourceforge.net/ 來了解有關熵收集守護程序源的信息。

可用性: 對于 LibreSSL 和 OpenSSL > 1.1.0 不可用。

ssl.RAND_add(bytes, entropy)?

將給定的 bytes 混合到 SSL 偽隨機數生成器中。 形參 entropy (float 類型) 是數據所包含的熵的下界 (因此你可以總是使用 0.0)。 請查看 RFC 1750 了解有關熵源的更多信息。

在 3.5 版更改: 現在支持可寫的 字節類對象

證書處理?

ssl.match_hostname(cert, hostname)?

驗證 cert (使用 SSLSocket.getpeercert() 所返回的已解碼格式) 是否匹配給定的 hostname。 所應用的規則是在 RFC 2818, RFC 5280RFC 6125 中描述的檢查 HTTPS 服務器身份的規則。 除了 HTTPS,此函數還應當適用于各種基于 SSL 協議的服務器身份檢查操作,例如 FTPS, IMAPS, POPS 等等。

失敗時引發 CertificateError。 成功時此函數無返回值:

>>> cert = {'subject': ((('commonName', 'example.com'),),)}
>>> ssl.match_hostname(cert, "example.com")
>>> ssl.match_hostname(cert, "example.org")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/py3k/Lib/ssl.py", line 130, in match_hostname
ssl.CertificateError: hostname 'example.org' doesn't match 'example.com'

3.2 新版功能.

在 3.3.3 版更改: 此函數現在遵循 RFC 6125, 6.4.3 小節,它不會匹配多個通配符 (例如 *.*.com*a*.example.org) 也不匹配國際化域名 (IDN) 片段內部的通配符。 IDN A 標簽例如 www*.xn--pthon-kva.org 仍然受支持,但 x*.python.org 不再能匹配 xn--tda.python.org

在 3.5 版更改: 現在支持匹配存在于證書的 subjectAltName 字段中的 IP 地址。

在 3.7 版更改: 此函數不再被用于 TLS 連接。 主機匹配現在是由 OpenSSL 執行的。

允許位于段的最左端且為唯一字符的通配符。 部分通配符例如 www*.example.com 已不再受支持。

3.7 版后已移除.

ssl.cert_time_to_seconds(cert_time)?

返回距離 Unix 紀元零時的秒數,給定的 cert_time 字符串代表來自證書的 "notBefore" 或 "notAfter" 日期值,采用 "%b %d %H:%M:%S %Y %Z" strptime 格式(C 區域)。

以下為示例代碼:

>>> 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, ca_certs=None)?

帶 SSL 保護的服務器的地址 addr 以 (hostname, port-number) 對的形式給出,獲取服務器的證書,并將其以 PEM 編碼字符串的形式返回。 如果指定了 ssl_version,則使用該版本的 SSL 協議嘗試連接服務器。 如果指定了 ca_certs,它應當是一個包含根證書列表的文件,使用與 SSLContext.wrap_socket() 中同名形參一致的格式。 該調用將嘗試根據指定的根證書集來驗證服務器證書,如果驗證失敗則該調用也將失敗。

在 3.3 版更改: 此函數現在是 IPv6 兼容的。-compatible.

在 3.5 版更改: 默認的 ssl_versionPROTOCOL_SSLv3 改為 PROTOCOL_TLS 以保證與現代服務器的最大兼容性。

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() 所使用的相同。 返回值是一個 named tuple DefaultVerifyPaths:

  • cafile - 解析出的 cafile 路徑或者如果文件不存在則為 None,

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

  • openssl_cafile_env - 指向一個 cafile 的 OpenSSL 環境鍵,

  • openssl_cafile - 一個 cafile 的硬編碼路徑,

  • openssl_capath_env - 指向一個 capath 的 OpenSSL 環境鍵,

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

可用性: LibreSSL 會忽略環境變量 openssl_cafile_envopenssl_capath_env

3.4 新版功能.

ssl.enum_certificates(store_name)?

從 Windows 的系統證書庫中檢索證書。 store_name 可以是 CA, ROOTMY 中的一個。 Windows 也可能會提供額外的證書庫。

此函數返回一個包含 (cert_bytes, encoding_type, trust) 元組的列表。 encoding_type 指明 cert_bytes 的編碼格式。 它可以為 x509_asn 以表示 X.509 ASN.1 數據或是 pkcs_7_asn 以表示 PKCS#7 ASN.1 數據。 trust 以 OIDS 集合的形式指明證書的目的,或者如果證書對于所有目的都可以信任則為 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 可以是 CA, ROOTMY 中的一個。 Windows 也可能會提供額外的證書庫。

此函數返回一個包含 (cert_bytes, encoding_type, trust) 元組的列表。 encoding_type 指明 cert_bytes 的編碼格式。 它可以為 x509_asn 以表示 X.509 ASN.1 數據或是 pkcs_7_asn 以表示 PKCS#7 ASN.1 數據。

可用性: Windows。

3.4 新版功能.

ssl.wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_TLS, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None)?

接受一個 socket.socket 的實例 sock,并返回一個 ssl.SSLSocket 的實例,該類型是 socket.socket 的子類型,它將下層的套接字包裝在一個 SSL 上下文中。 sock 必須是一個 SOCK_STREAM 套接字;其他套接字類型不被支持。

在內部,該函數會創建一個 SSLContext,其協議版本為 ssl_versionSSLContext.options 設為 cert_reqs。 如果設置了 keyfile, certfile, ca_certsciphers 等形參,則參數值會被傳給 SSLContext.load_cert_chain(), SSLContext.load_verify_locations() 以及 SSLContext.set_ciphers()

參數 server_side, do_handshake_on_connectsuppress_ragged_eofs 具有與 SSLContext.wrap_socket() 相同的含義。

3.7 版后已移除: 從 Python 3.2 和 2.7.9 開始,建議使用 SSLContext.wrap_socket() 來代替 wrap_socket()。 模塊級函數的功能受限并且將創建不安全的客戶端套接字,不帶服務器名稱提示或主機名匹配。

常量?

所有常量現在都是 enum.IntEnumenum.IntFlag 多項集的成員。

3.6 新版功能.

ssl.CERT_NONE?

SSLContext.verify_modewrap_socket()cert_reqs 形參可能的取值。 PROTOCOL_TLS_CLIENT 除外,這是默認的模式。 對于客戶端套接字,幾乎任何證書都是可接受的。 驗證錯誤例如不受信任或過期的證書錯誤會被忽略并且不會中止 TLS/SSL 握手。

在服務器模式下,不會從客戶端請求任何證書,因此客戶端不會發送任何用于客戶端證書身份驗證的證書。

參見下文對于 安全考量 的討論。

ssl.CERT_OPTIONAL?

SSLContext.verify_modewrap_socket()cert_reqs 形參可能的取值。 CERT_OPTIONAL 具有與 CERT_REQUIRED 相同的含義。 對于客戶端套接字推薦改用 CERT_REQUIRED

在服務器模式下,客戶端證書請求會被發送給客戶端。 客戶端可以忽略請求也可以發送一個證書以執行 TLS 客戶端證書身份驗證。 如果客戶端選擇發送證書,則將對其執行驗證。 任何驗證錯誤都將立即中止 TLS 握手。

使用此設置要求將一組有效的 CA 證書傳遞給 SSLContext.load_verify_locations() 或是作為 wrap_socket()ca_certs 形參值。

ssl.CERT_REQUIRED?

SSLContext.verify_modewrap_socket()cert_reqs 形參可能的取值。 在此模式下,需要從套接字連接的另一端獲取證書;如果未提供證書或驗證失敗則將引發 SSLError。 此模式 不能 在客戶端模式下對證書進行驗證,因為它不會匹配主機名。 check_hostname 也必須被啟用以驗證證書的真實性。 PROTOCOL_TLS_CLIENT 會使用 CERT_REQUIRED 并默認啟用 check_hostname

對于服務器套接字,此模式會提供強制性的 TLS 客戶端證書驗證。 客戶端證書請求會被發送給客戶端并且客戶端必須提供有效且受信任的證書。

使用此設置要求將一組有效的 CA 證書傳遞給 SSLContext.load_verify_locations() 或是作為 wrap_socket()ca_certs 形參值。

class ssl.VerifyMode?

CERT_* 常量的 enum.IntEnum 多項集。

3.6 新版功能.

ssl.VERIFY_DEFAULT?

SSLContext.verify_flags 可能的取值。 在此模式下,證書吊銷列表(CRL)并不會被檢查。 OpenSSL 默認不要求也不驗證 CRL。

3.4 新版功能.

ssl.VERIFY_CRL_CHECK_LEAF?

Possible value for SSLContext.verify_flags. In this mode, only the peer cert is checked but none of the intermediate CA certificates. The mode requires a valid CRL that is signed by the peer cert's issuer (its direct ancestor CA). If no proper CRL has has been loaded with SSLContext.load_verify_locations, validation will fail.

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_X509_TRUSTED_FIRST?

SSLContext.verify_flags 可能的取值。 它指示 OpenSSL 在構建用于驗證某個證書的信任鏈時首選受信任的證書。 此旗標將默認被啟用。

3.4.4 新版功能.

class ssl.VerifyFlags?

VERIFY_* 常量的 enum.IntFlag 多項集。

3.6 新版功能.

ssl.PROTOCOL_TLS?

選擇客戶端和服務器均支持的最高協議版本。 此選項名稱并不準確,實際上 "SSL" 和 "TLS" 協議均可被選擇。

3.6 新版功能.

ssl.PROTOCOL_TLS_CLIENT?

PROTOCOL_TLS 一樣地自動協商最高協議版本,但是只支持客戶端 SSLSocket 連接。 此協議默認會啟用 CERT_REQUIREDcheck_hostname

3.6 新版功能.

ssl.PROTOCOL_TLS_SERVER?

PROTOCOL_TLS 一樣地自動協商最高協議版本,但是只支持服務器 SSLSocket 連接。

3.6 新版功能.

ssl.PROTOCOL_SSLv23?

PROTOCOL_TLS 的別名。

3.6 版后已移除: 請改用 PROTOCOL_TLS

ssl.PROTOCOL_SSLv2?

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

如果 OpenSSL 編譯時附帶了 OPENSSL_NO_SSL2 旗標則此協議將不可用。

警告

SSL 版本 2 并不安全。 極不建議使用它。

3.6 版后已移除: OpenSSL 已經移除了對 SSLv2 的支持。

ssl.PROTOCOL_SSLv3?

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

如果 OpenSSL 編譯時使用了 OPENSSL_NO_SSLv3 旗標則此協議將不可用。

警告

SSL 版本 3 并不安全。 極不建議使用它。

3.6 版后已移除: OpenSSL 已經棄用了所有帶有特定版本號的協議。 請改用默認協議 PROTOCOL_TLS 并附帶 OP_NO_SSLv3 等旗標。

ssl.PROTOCOL_TLSv1?

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

3.6 版后已移除: OpenSSL 已經棄用了所有帶有特定版本號的協議。 請改用默認協議 PROTOCOL_TLS 并附帶 OP_NO_SSLv3 等旗標。

ssl.PROTOCOL_TLSv1_1?

選擇 TLS 版本 1.1 作為通道加密協議。 僅適用于 openssl 版本 1.0.1+。

3.4 新版功能.

3.6 版后已移除: OpenSSL 已經棄用了所有帶有特定版本號的協議。 請改用默認協議 PROTOCOL_TLS 并附帶 OP_NO_SSLv3 等旗標。

ssl.PROTOCOL_TLSv1_2?

選譯 TLS 版本 1.2 作為通道加密協議。 這是最新的版本,也應是能提供最大保護的最佳選擇,如果通信雙方都支持它的話。 僅適用于 openssl 版本 1.0.1+。

3.4 新版功能.

3.6 版后已移除: OpenSSL 已經棄用了所有帶有特定版本號的協議。 請改用默認協議 PROTOCOL_TLS 并附帶 OP_NO_SSLv3 等旗標。

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.7 新版功能.

3.7 版后已移除: 此選項自 OpenSSL 1.1.0 起已被棄用。 它被添加到 2.7.15, 3.6.3 和 3.7.0 是為了向下兼容 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 握手中發送虛擬更改密碼規格(CCS)消息以使得 TLS 1.3 連接看起來更像是 TLS 1.2 連接。

此選項僅適用于 OpenSSL 1.1.1 及更新的版本。

3.8 新版功能.

ssl.OP_NO_COMPRESSION?

在 SSL 通道上禁用壓縮。 這適用于應用協議支持自己的壓縮方案的情況。

此選項僅適用于 OpenSSL 1.0.0 及更新的版本。

3.3 新版功能.

class ssl.Options?

OP_* 常量的 enum.IntFlag 多項集。

ssl.OP_NO_TICKET?

阻止客戶端請求會話憑據。

3.6 新版功能.

ssl.HAS_ALPN?

OpenSSL 庫是否具有對 RFC 7301 中描述的 應用層協議協商 TLS 擴展的內置支持。

3.5 新版功能.

ssl.HAS_NEVER_CHECK_COMMON_NAME?

OpenSSL 庫是否具有對不檢測目標通用名稱的內置支持且 SSLContext.hostname_checks_common_name 為可寫狀態。

3.7 新版功能.

ssl.HAS_ECDH?

OpenSSL 庫是否具有對基于橢圓曲線的 Diffie-Hellman 密鑰交換的內置支持。 此常量應當為真值,除非發布者明確地禁用了此功能。

3.3 新版功能.

ssl.HAS_SNI?

OpenSSL 庫是否具有對 服務器名稱提示 擴展(在 RFC 6066 中定義)的內置支持。

3.2 新版功能.

ssl.HAS_NPN?

OpenSSL 庫是否具有對 應用層協議協商 中描述的 下一協議協商 的內置支持。 當此常量為真值時,你可以使用 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.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 Alert Registry 中包含了這個列表及對定義其含義的 RFC 引用。

被用作 SSLContext.set_servername_callback() 中的回調函數的返回值。

3.4 新版功能.

class ssl.AlertDescription?

ALERT_DESCRIPTION_* 常量的 enum.IntEnum 多項集。

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?

SSL_ERROR_* 常量的 enum.IntEnum 多項集。

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。

SSL 套接字?

class ssl.SSLSocket(socket.socket)?

SSL 套接字提供了 套接字對象 的下列方法:

但是,由于 SSL(和 TLS)協議在 TCP 之上具有自己的框架,因此 SSL 套接字抽象在某些方面可能與常規的 OS 層級套接字存在差異。 特別是要查看 非阻塞型套接字說明

SSLSocket 的實例必須使用 SSLContext.wrap_socket() 方法來創建。

在 3.5 版更改: 新增了 sendfile() 方法。

在 3.5 版更改: shutdown() 不會在每次接收或發送字節數據后重置套接字超時。 現在套接字超時為關閉的最大總持續時間。

3.6 版后已移除: 直接創建 SSLSocket 實例的做法已被棄用,請使用 SSLContext.wrap_socket() 來包裝套接字。

在 3.7 版更改: SSLSocket 的實例必須使用 wrap_socket() 來創建。 在較早的版本中,直接創建實例是可能的。 但這從未被記入文檔或是被正式支持。

SSL 套接字還具有下列方法和屬性:

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

從 SSL 套接字讀取至多 len 個字節的數據并將結果作為 bytes 實例返回。 如果指定了 buffer,則改為讀取到緩沖區,并返回所讀取的字節數。

如果套接字為 非阻塞型 則會引發 SSLWantReadErrorSSLWantWriteError 且讀取將阻塞。

由于在任何時候重新協商都是可能的,因此調用 read() 也可能導致寫入操作。

在 3.5 版更改: 套接字超時在每次接收或發送字節數據后不會再被重置。 現在套接字超時為讀取至多 len 個字節數據的最大總持續時間。

3.6 版后已移除: 請使用 recv() 來代替 read()

SSLSocket.write(buf)?

buf 寫入到 SSL 套接字并返回所寫入的字節數。 buf 參數必須為支持緩沖區接口的對象。

如果套接字為 非阻塞型 則會引發 SSLWantReadErrorSSLWantWriteError 且讀取將阻塞。

由于在任何時候重新協商都是可能的,因此調用 write() 也可能導致讀取操作。

在 3.5 版更改: 套接字超時在每次接收或發送字節數據后不會再被重置。 現在套接字超時為寫入 buf 的最大總持續時間。

3.6 版后已移除: 請使用 send() 來代替 write()

注解

read()write() 方法是讀寫未加密的應用級數據,并將其解密/加密為帶加密的線路級數據的低層級方法。 這些方法需要有激活的 SSL 連接,即握手已完成而 SSLSocket.unwrap() 尚未被調用。

通常你應當使用套接字 API 方法例如 recv()send() 來代替這些方法。

SSLSocket.do_handshake()?

執行 SSL 設置握手。

在 3.4 版更改: 當套接字的 contextcheck_hostname 屬性為真值時此握手方法還會執行 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 實例。 如果證書未通過驗證,則字典將為空。 如果證書通過驗證,它將返回由多個密鑰組成的字典,其中包括 subject (證書頒發給的主體) 和 issuer (頒發證書的主體)。 如果證書包含一個 Subject Alternative Name 擴展的實例 (see RFC 3280),則字典中還將有一個 subjectAltName 鍵。

subjectissuer 字段都是包含在證書中相應字段的數據結構中給出的相對專有名稱(RDN)序列的元組,每個 RDN 均為 name-value 對的序列。 這里是一個實際的示例:

{'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}

注解

要驗證特定服務的證書,你可以使用 match_hostname() 函數。

如果 binary_form 形參為 True,并且提供了證書,此方法會將整個證書的 DER 編碼形式作為字節序列返回,或者如果對等方未提供證書則返回 None。 對等方是否提供證書取決于 SSL 套接字的角色:

  • 對于客戶端 SSL 套接字,服務器將總是提供證書,無論是否需要進行驗證;

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

在 3.2 版更改: 返回的字典包括額外的條目例如 issuernotBefore

在 3.4 版更改: 如果握手未完成則會引發 ValueError。 返回的字典包括額外的 X509v3 擴展條目例如 crlDistributionPoints, caIssuersOCSP URI。

在 3.7.6 版更改: IPv6 地址字符串不再附帶末尾換行符。

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()?

返回在Return the higher-level protocol that was selected during the TLS/SSL 握手期間所選擇的高層級協議。 如果 SSLContext.set_npn_protocols() 未被調用,或者如果另一方不支持 NPN,或者如果握手尚未發生,則將返回 None

3.3 新版功能.

SSLSocket.unwrap()?

執行 SSL 關閉握手,這會從下層的套接字中移除 TLS 層,并返回下層的套接字對象。 這可被用來通過一個連接將加密操作轉為非加密。 返回的套接字應當總是被用于同連接另一方的進一步通信,而不是原始的套接字。

SSLSocket.verify_client_post_handshake()?

向一個 TLS 1.3 客戶端請求握手后身份驗證(PHA)。 只有在初始 TLS 握手之后且雙方都啟用了 PHA 的情況下才能為服務器端套接字的 TLS 1.3 連接啟用 PHA,參見 SSLContext.post_handshake_auth

此方法不會立即執行證書交換。 服務器端會在下一次寫入事件期間發送 CertificateRequest 并期待客戶端在下一次讀取事件期間附帶證書進行響應。

如果有任何前置條件未被滿足(例如非 TLS 1.3,PHA 未啟用),則會引發 SSLError

注解

僅在 OpenSSL 1.1.1 且 TLS 1.3 被啟用時可用。 沒有 TLS 1.3 支持,此方法將引發 NotImplementedError

3.7.1 新版功能.

SSLSocket.version()?

以字符串形式返回由連接協商確定的實際 SSL 協議版本,或者如果未建立安全連接則返回 None。 在撰寫本文檔時,可能的返回值包括 "SSLv2", "SSLv3", "TLSv1", "TLSv1.1""TLSv1.2"。 最新的 OpenSSL 版本可能會定義更多的返回值。

3.5 新版功能.

SSLSocket.pending()?

返回在連接上等待被讀取的已解密字節數。

SSLSocket.context?

此 SSL 套接字所聯結的 SSLContext 對象。 如果 SSL 套接字是使用已棄用的 wrap_socket() 函數 (而非 SSLContext.wrap_socket()) 創建的,則這將是為此 SSL 套接字創建的自定義上下文對象。

3.2 新版功能.

SSLSocket.server_side?

一個布爾值,對于服務器端套接字為 True 而對于客戶端套接字則為 False

3.2 新版功能.

SSLSocket.server_hostname?

服務器的主機名: str 類型,對于服務器端套接字或者如果構造器中未指定主機名則為 None

3.2 新版功能.

在 3.7 版更改: 現在該屬性將始終為 ASCII 文本。 當 server_hostname 為一個國際化域名(IDN)時,該屬性現在會保存為 A 標簽形式 ("xn--pythn-mua.org") 而非 U 標簽形式 ("pyth?n.org")。

SSLSocket.session?

用于 SSL 連接的 SSLSession。 該會話將在執行 TLS 握手后對客戶端和服務器端套接字可用。 對于客戶端套接字該會話可以在調用 do_handshake() 之前被設置以重用一個會話。

3.6 新版功能.

SSLSocket.session_reused?

3.6 新版功能.

SSL 上下文?

3.2 新版功能.

SSL 上下文可保存各種比單獨 SSL 連接壽命更長的數據,例如 SSL 配置選項,證書和私鑰等。 它還可為服務器端套接字管理緩存,以加快來自相同客戶端的重復連接。

class ssl.SSLContext(protocol=PROTOCOL_TLS)?

創建一個新的 SSL 上下文。 你可以傳入 protocol,它必須為此模塊中定義的 PROTOCOL_* 常量之一。 該形參指定要使用哪個 SSL 協議版本。 通常,服務器會選擇一個特定的協議版本,而客戶端必須適應服務器的選擇。 大多數版本都不能與其他版本互操作。 如果未指定,則默認值為 PROTOCOL_TLS;它提供了與其他版本的最大兼容性。

這個表顯示了客戶端(橫向)的哪個版本能夠連接服務器(縱向)的哪個版本。

client / server

SSLv2

SSLv3

TLS 3

TLSv1

TLSv1.1

TLSv1.2

SSLv2

no 1

SSLv3

no 2

TLS (SSLv23) 3

no 1

no 2

TLSv1

TLSv1.1

TLSv1.2

備注

1(1,2)

SSLContext 默認設置 OP_NO_SSLv2 以禁用 SSLv2。

2(1,2)

SSLContext 默認設置 OP_NO_SSLv3 以禁用 SSLv3。

3(1,2)

TLS 1.3 協議在 OpenSSL >= 1.1.1 中設置 PROTOCOL_TLS 時可用。 沒有專門針對 TLS 1.3 的 PROTOCOL 常量。

參見

create_default_context()ssl 為特定目標選擇安全設置。

在 3.6 版更改: 上下文會使用安全默認值來創建。 默認設置的選項有 OP_NO_COMPRESSION, OP_CIPHER_SERVER_PREFERENCE, OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE, OP_NO_SSLv2 (except for PROTOCOL_SSLv2) 和 OP_NO_SSLv3 (except for PROTOCOL_SSLv3)。 初始密碼集列表只包含 HIGH 密碼,不包含 NULL 密碼和 MD5 密碼 (PROTOCOL_SSLv2 除外)。

SSLContext 對象具有以下方法和屬性:

SSLContext.cert_store_stats()?

獲取以字典表示的有關已加載的 X.509 證書數量,被標記為 CA 證書的 X.509 證書數量以及證書吊銷列表的的統計信息。

具有一個 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 參數可以是一個函數,調用時將得到用于解密私鑰的密碼。 它在私鑰被加密且需要密碼時才會被調用。 它調用時將不帶任何參數,并且應當返回一個字符串、字節串或字節數組。 如果返回值是一個字符串,在用它解密私鑰之前它將以 UTF-8 進行編碼。 或者也可以直接將字符串、字節串或字節數組值作為 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)?

verify_mode 不為 CERT_NONE 時加載一組用于驗證其他對等方證書的 "證書頒發機構" (CA) 證書。 必須至少指定 cafilecapath 中的一個。

此方法還可加載 PEM 或 DER 格式的證書吊銷列表 (CRL),為此必須正確配置 SSLContext.verify_flags

如果存在 cafile 字符串,它應為 PEM 格式的級聯 CA 證書文件的路徑。 請參閱 證書 中的討論來了解有關如何處理此文件中的證書的更多信息。

如果存在 capath 字符串,它應為包含多個 PEM 格式的 CA 證書的目錄的路徑,并遵循 OpenSSL 專屬布局

如果存在 cadata 對象,它應為一個或多個 PEM 編碼的證書的 ASCII 字符串或者 DER 編碼的證書的 bytes-like object。 與 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()  # OpenSSL 1.0.x
[{'alg_bits': 256,
  'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(256) Mac=AEAD',
  'id': 50380848,
  'name': 'ECDHE-RSA-AES256-GCM-SHA384',
  'protocol': 'TLSv1/SSLv3',
  'strength_bits': 256},
 {'alg_bits': 128,
  'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(128) Mac=AEAD',
  'id': 50380847,
  'name': 'ECDHE-RSA-AES128-GCM-SHA256',
  'protocol': 'TLSv1/SSLv3',
  'strength_bits': 128}]

在 OpenSSL 1.1 及更新的版本中密碼字典會包含額外的字段:

>>> ctx.get_ciphers()  # OpenSSL 1.1+
[{'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'}]

可用性: OpenSSL 1.0.2+。

3.6 新版功能.

SSLContext.set_default_verify_paths()?

從構建 OpenSSL 庫時定義的文件系統路徑中加載一組默認的 "證書頒發機構" (CA) 證書。 不幸的是,沒有一種簡單的方式能知道此方法是否執行成功:如果未找到任何證書也不會返回錯誤。 不過,當 OpenSSL 庫是作為操作系統的一部分被提供時,它的配置應當是正確的。

SSLContext.set_ciphers(ciphers)?

為使用此上下文創建的套接字設置可用密碼。 它應當為 OpenSSL 密碼列表格式 的字符串。 如果沒有可被選擇的密碼(由于編譯時選項或其他配置禁止使用所指定的任何密碼),則將引發 SSLError

注解

在連接后,SSL 套接字的 SSLSocket.cipher() 方法將給出當前所選擇的密碼。

在默認情況下 OpenSSL 1.1.1 會啟用 TLS 1.3 密碼套件。 該套件不能通過 set_ciphers() 來禁用。

SSLContext.set_alpn_protocols(protocols)?

指定在 SSL/TLS 握手期間套接字應當通告的協議。 它應為由 ASCII 字符串組成的列表,例如 ['http/1.1', 'spdy/2'],按首選順序排列。 協議的選擇將在握手期間發生,并依據 RFC 7301 來執行。 在握手成功后,SSLSocket.selected_alpn_protocol() 方法將返回已達成一致的協議。

如果 HAS_ALPNFalse 則此方法將引發 NotImplementedError

當雙方都支持 ALPN 但不能就協議達成一致時 OpenSSL 1.1.0 至 1.1.0e 將中止并引發 SSLError。 1.1.0f+ 的行為類似于 1.0.2,SSLSocket.selected_alpn_protocol() 返回 None。

3.5 新版功能.

SSLContext.set_npn_protocols(protocols)?

指定在Specify which protocols the socket should advertise during the SSL/TLS 握手期間套接字應當通告的協議。 它應為由字符串組成的列表,例如 ['http/1.1', 'spdy/2'],按首選順序排列。 協議的選擇將在握手期間發生,并將依據 應用層協議協商 來執行。 在握手成功后,SSLSocket.selected_npn_protocol() 方法將返回已達成一致的協議。

如果 HAS_NPNFalse 則此方法將引發 NotImplementedError

3.3 新版功能.

SSLContext.sni_callback?

注冊一個回調函數,當 TLS 客戶端指定了一個服務器名稱提示時,該回調函數將在 SSL/TLS 服務器接收到 TLS Client Hello 握手消息后被調用。 服務器名稱提示機制的定義見 RFC 6066 section 3 - Server Name Indication。

每個 SSLContext 只能設置一個回調。 如果 sni_callback 被設置為 None 則會禁用回調。 對該函數的后續調用將禁用之前注冊的回調。

此回調函數將附帶三個參數來調用;第一個參數是 ssl.SSLSocket,第二個參數是代表客戶端準備與之通信的服務器的字符串 (或者如果 TLS Client Hello 不包含服務器名稱則為 None) 而第三個參數是原來的 SSLContext。 服務器名稱參數為文本形式。 對于國際化域名,服務器名稱是一個 IDN A 標簽 ("xn--pythn-mua.org")。

此回調的一個典型用法是將 ssl.SSLSocketSSLSocket.context 屬性修改為一個 SSLContext 類型的新對象,該對象代表與服務器相匹配的證書鏈。

由于 TLS 連接處于早期協商階段,因此僅能使用有限的方法和屬性例如 SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert(), SSLSocket.getpeercert(), SSLSocket.cipher()SSLSocket.compress() 方法要求 TLS 連接已經過 TLS Client Hello 因而將既不包含返回有意義的值,也不能安全地調用它們。

sni_callback 函數必須返回 None 以允許 TLS 協商繼續進行。 如果想要 TLS 失敗,則可以返回常量 ALERT_DESCRIPTION_*。 其他返回值將導致 TLS 的致命錯誤 ALERT_DESCRIPTION_INTERNAL_ERROR.

如果從 sni_callback 函數引發了異常,則 TLS 連接將終止并發出 TLS 致命警告消息 ALERT_DESCRIPTION_HANDSHAKE_FAILURE

如果 OpenSSL library 庫在構建時定義了 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 標簽 ("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)?

為基于橢圓曲線的 Elliptic Curve-based 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_side 為真值則指定 server_hostname 將引發 ValueError

形參 do_handshake_on_connect 指明是否要在調用 socket.connect() 之后自動執行 SSL 握手,還是要通過發起調用 SSLSocket.do_handshake() 方法讓應用程序顯式地調用它。 顯式地調用 SSLSocket.do_handshake() 可給予程序對握手中所涉及的套接字 I/O 阻塞行為的控制。

形參 suppress_ragged_eofs 指明 SSLSocket.recv() 方法應當如何從連接的另一端發送非預期的 EOF 信號。 如果指定為 True (默認值),它將返回正常的 EOF (空字節串對象) 來響應從下層套接字引發的非預期的 EOF 錯誤;如果指定為 False,它將向調用方引發異常。

session,參見 session

在 3.5 版更改: 總是允許傳送 server_hostname,即使 OpenSSL 沒有 SNI。

在 3.6 版更改: 增加了 session 參數。

在 3.7 版更改: 此方法返回 SSLContext.sslsocket_class 的實例而非硬編碼的 SSLSocket

SSLContext.sslsocket_class?

SSLContext.wrap_socket() 的返回類型,默認為 SSLSocket。 該屬性可以在類實例上被重載以便返回自定義的 SSLSocket 的子類。

3.7 新版功能.

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

包裝 BIO 對象 incomingoutgoing 并返回一個 SSLContext.sslobject_class (默認為 SSLObject) 的實例。 SSL 例程將從 BIO 中讀取輸入數據并將數據寫入到 outgoing BIO。

server_side, server_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() 中的 match_hostname() 進行匹配。 上下文的 verify_mode 必須被設為 CERT_OPTIONALCERT_REQUIRED,并且你必須將 server_hostname 傳給 wrap_socket() 以便匹配主機名。 啟用主機名檢查會自動將 sets verify_modeCERT_NONE 設置為 CERT_REQUIRED。 只要啟用了主機名檢查就不能將其設置回 CERT_NONEPROTOCOL_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_NONEverify_mode 會自動更改為 CERT_REQUIRED。 在之前版本中同樣的操作將失敗并引發 ValueError

注解

此特性要求 OpenSSL 0.9.8f 或更新的版本。

SSLContext.maximum_version?

一個代表所支持的最高 TLS 版本的 TLSVersion 枚舉成員。 該值默認為 TLSVersion.MAXIMUM_SUPPORTED。 這個屬性對于 PROTOCOL_TLS, PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 以外的其他協議來說都是只讀的。

maximum_version, minimum_versionSSLContext.options 等屬性都會影響上下文所支持的 SSL 和 TLS 版本。 這個實現不會阻止無效的組合。 例如一個 optionsOP_NO_TLSv1_2maximum_version 設為 TLSVersion.TLSv1_2 的上下文將無法建立 TLS 1.2 連接。

注解

除非 ssl 模塊使用 OpenSSL 1.1.0g 或更新的版本編譯否則這個屬性將不可用。

3.7 新版功能.

SSLContext.minimum_version?

SSLContext.maximum_version 類似,區別在于它是所支持的最低版本或為 TLSVersion.MINIMUM_SUPPORTED

注解

除非 ssl 模塊使用 OpenSSL 1.1.0g 或更新的版本編譯否則這個屬性將不可用。

3.7 新版功能.

SSLContext.options?

一個代表此上下文中所啟用的 SSL 選項集的整數。 默認值為 OP_ALL,但你也可以通過在選項間進行 OR 運算來指定其他選項例如 OP_NO_SSLv2

注解

對于 0.9.8m 之前的 OpenSSL 版本,只能設置選項,而不能清除它們。 嘗試(通過重置相應比特位)清除選項將會引發 ValueError

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

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

啟用 TLS 1.3 握手后客戶端身份驗證。 握手后驗證默認是被禁用的,服務器只能在初始握手期間請求 TLS 客戶端證書。 當啟用時,服務器可以在握手之后的任何時候請求 TLS 客戶端證書。

當在客戶端套接字上啟用時,客戶端會向服務器發信號說明它支持握手后身份驗證。

當在服務器端套接字上啟用時,SSLContext.verify_mode 也必須被設為 CERT_OPTIONALCERT_REQUIRED。 實際的客戶端證書交換會被延遲直至 SSLSocket.verify_client_post_handshake() 被調用并執行了一些 I/O 操作后再進行。

注解

僅在 OpenSSL 1.1.1 且 TLS 1.3 被啟用時可用。 如果沒有 TLS 1.3 支持,該屬性值將為 None 且不可被更改

3.7.1 新版功能.

SSLContext.protocol?

構造上下文時所選擇的協議版本。 這個屬性是只讀的。

SSLContext.hostname_checks_common_name?

在沒有目標替代名稱擴展的情況下 check_hostname 是否要回退為驗證證書的通用名稱(默認為真值)。

注解

僅在 OpenSSL 1.1.0 或更新的版本上可寫。

3.7 新版功能.

SSLContext.verify_flags?

證書驗證操作的旗標。 你可以通過對 VERIFY_CRL_CHECK_LEAF 等值執行 OR 運算來設置組合旗標。 在默認情況下 OpenSSL 不會要求也不會驗證證書吊銷列表(CRL)。 僅在 openssl 版本 0.9.8+ 上可用。

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_OPTIONALCERT_REQUIRED 之一。

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

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

證書?

總的來說證書是公鑰/私鑰系統的一個組成部分。 在這個系統中,每 個 主體 (可能是一臺機器、一個人或者一個組織) 都會分配到唯一的包含兩部分的加密密鑰。 一部分密鑰是公開的,稱為 公鑰;另一部分密鑰是保密的,稱為 私鑰。 這兩個部分是互相關聯的,就是說如果你用其中一個部分來加密一條消息,你將能用并且 只能 用另一個部分來解密它。

在一個證書中包含有兩個主體的相關信息。 它包含 目標方 的名稱和目標方的公鑰。 它還包含由第二個主體 頒發方 所發布的聲明:目標方的身份與他們所宣稱的一致,包含的公鑰也確實是目標方的公鑰。 頒發方的聲明使用頒發方的私鑰進行簽名,該私鑰的內容只有頒發方自己才知道。 但是,任何人都可以找到頒發方的公鑰,用它來解密這個聲明,并將其與證書中的其他信息進行比較來驗證頒發方聲明的真實性。 證書還包含有關其有效期限的信息。 這被表示為兩個字段,即 "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() 自動完成。

合并的密鑰和證書?

私鑰往往與證書存儲在相同的文件中;在此情況下,只需要將 certfile 形參傳給 SSLContext.load_cert_chain()wrap_socket()。 如果私鑰是與證書一起存儲的,則它應當放在證書鏈的第一個證書之前:

-----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.mydomain.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 套接字的行為與常規套接字略有不同。 當使用非阻塞模式時,你需要注意下面這些事情:

  • 如果一個 I/O 操作會阻塞,大多數 SSLSocket 方法都將引發 SSLWantWriteErrorSSLWantReadError 而非 BlockingIOError。 如果有必要在下層套接字上執行讀取操作將引發 SSLWantReadError,在下層套接字上執行寫入操作則將引發 SSLWantWriteError。 請注意嘗試 寫入 到 SSL 套接字可能需要先從下層套接字 讀取,而嘗試從 SSL 套接字 讀取 則可能需要先向下層套接字 寫入

    在 3.5 版更改: 在較早的 Python 版本中,SSLSocket.send() 方法會返回零值而非引發 SSLWantWriteErrorSSLWantReadError

  • 調用 select() 將告訴你可以從 OS 層級的套接字讀取(或向其寫入),但這并不意味著在上面的 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 模塊來輪詢事件并處理 SSLWantWriteError, SSLWantReadErrorBlockingIOError 等異常。 它還會異步地執行 SSL 握手。

內存 BIO 支持?

3.5 新版功能.

自從 SSL 模塊在 Python 2.6 起被引入之后,SSLSocket 類提供了兩個互相關聯但彼此獨立的功能分塊:

  • SSL 協議處理

  • 網絡 IO

網絡 IO API 與 socket.socket 所提供的功能一致,SSLSocket 也是從那里繼承而來的。 這允許 SSL 套接字被用作常規套接字的替代,使得向現有應用程序添加 SSL 支持變得非常容易。

將 SSL 協議處理與網絡 IO 結合使用通常都能運行良好,但在某些情況下則不能。 此情況的一個例子是 async IO 框架,該框架要使用不同的 IO 多路復用模型而非 (基于就緒狀態的) "在文件描述器上執行選擇/輪詢" 模型,該模型是 socket.socket 和內部 OpenSSL 套接字 IO 例程正常運行的假設前提。 這種情況在該模型效率不高的 Windows 平臺上最為常見。 為此還提供了一個 SSLSocket 的簡化形式,稱為 SSLObject

class ssl.SSLObject?

SSLSocket 的簡化形式,表示一個不包含任何網絡 IO 方法的 SSL 協議實例。 這個類通常由想要通過內存緩沖區為 SSL 實現異步 IO 的框架作者來使用。

這個類在低層級 SSL 對象上實現了一個接口,與 OpenSSL 所實現的類似。 此對象會捕獲 SSL 連接的狀態但其本身不提供任何網絡 IO。 IO 需要通過單獨的 "BIO" 對象來執行,該對象是 OpenSSL 的 IO 抽象層。

這個類沒有公有構造器。 SSLObject 實例必須使用 wrap_bio() 方法來創建。 此方法將創建 SSLObject 實例并將其綁定到一個 BIO 對。 其中 incoming BIO 用來將數據從 Python 傳遞到 SSL 協議實例,而 outgoing BIO 用來進行數據反向傳遞。

可以使用以下方法:

SSLSocket 相比,此對象缺少下列特性:

  • 任何形式的網絡 IO; recv()send() 僅對下層的 MemoryBIO 緩沖區執行讀取和寫入。

  • 不存在 do_handshake_on_connect 機制。 你必須總是手動調用 do_handshake() 來開始握手操作。

  • 不存在對 suppress_ragged_eofs 的處理。 所有違反協議的文件結束條件將通過 SSLEOFError 異常來報告。

  • 方法 unwrap() 的調用不返回任何東西,不會如 SSL 套接字那樣返回下層的套接字。

  • server_name_callback 回調被傳給 SSLContext.set_servername_callback() 時將獲得一個 SSLObject 實例而非 SSLSocket 實例作為其第一個形參。

有關 SSLObject 用法的一些說明:

在 3.7 版更改: SSLObject 的實例必須使用 wrap_bio() 來創建。 在較早的版本中,直接創建實例是可能的。 但這從未被記入文檔或是被正式支持。

SSLObject 會使用內存緩沖區與外部世界通信。 MemoryBIO 類提供了可被用于此目的的內存緩沖區。 它包裝了一個 OpenSSL 內存 BIO (Basic IO) 對象:

class ssl.MemoryBIO?

一個可被用來在 Python 和 SSL 協議實例之間傳遞數據的內存緩沖區。

pending?

返回當前存在于內存緩沖區的字節數。

eof?

一個表明內存 BIO 目前是否位于文件末尾的布爾值。

read(n=-1)?

從內存緩沖區讀取至多 n 個字節。 如果 n 未指定或為負值,則返回全部字節數據。

write(buf)?

將字節數據從 buf 寫入到內存 BIO。 buf 參數必須為支持緩沖區協議的對象。

返回值為寫入的字節數,它總是與 buf 的長度相等。

write_eof()?

將一個 EOF 標記寫入到內存 BIO。 在此方法被調用以后,再調用 write() 將是非法的。 屬性 eof will 在緩沖區當前的所有數據都被讀取之后將變為真值。

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() 來獲取并匹配目標服務。 對于許多協議和應用來說,服務可通過主機名來標識;在此情況下,可以使用 match_hostname() 函數。 這種通用檢測會在 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.options |= ssl.OP_NO_TLSv1
>>> client_context.options |= ssl.OP_NO_TLSv1_1

前面創建的 SSL 上下文將只允許 TLSv1.2 及更新版本(如果你的系統支持)的服務器連接。 PROTOCOL_TLS_CLIENT 默認會使用證書驗證和主機名檢查。 你必須將證書加載到上下文中。

密碼選擇?

如果你有更高級的安全要求,也可以通過 SSLContext.set_ciphers() 方法在協商 SSL 會話時對所啟用的加密進行微調。 從 Python 3.2.3 開始,ssl 默認會禁用某些較弱的加密,但你還可能希望進一步限制加密選項。 請確保仔細閱讀 OpenSSL 文檔中有關 加密列表格式 的部分。 如果你想要檢查給定的加密列表啟用了哪些加密,可以使用 SSLContext.get_ciphers() 或所在系統的 openssl ciphers 命令。

多進程?

如果使用此模塊作為多進程應用的一部分(例如使用 multiprocessingconcurrent.futures 模塊),請注意 OpenSSL 的內部隨機數字生成器并不能正確處理分支進程。 應用程序必須修改父進程的 PRNG 狀態,如果它們要使用任何包含 os.fork() 的 SSL 特性的話。 任何對 RAND_add(), RAND_bytes()RAND_pseudo_bytes() 都可以 。

TLS 1.3?

3.7 新版功能.

Python 通過 OpenSSL 1.1.1 提供了臨時性和實驗性的 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 客戶端證書請求、簽名算法配置和密鑰重生成等 TLS 1.3 特性尚未被支持。

LibreSSL 支持?

LibreSSL 是 OpenSSL 1.0.1 的一個分支。 ssl 模塊包含對 LibreSSL 的有限支持。 當 ssl 模塊使用 LibreSSL 進行編譯時某些特性將不可用。