ssl - 套接字对象的 TLS/SSL 包装器

源代码: Lib/ssl.py


此模块提供对网络套接字的传输层安全(通常称为“安全套接字层”)加密和对等身份验证功能的访问,包括客户端和服务器端。此模块使用 OpenSSL 库。它适用于所有现代 Unix 系统、Windows、macOS 以及可能的其他平台,只要在该平台上安装了 OpenSSL。

注意

某些行为可能与平台相关,因为调用的是操作系统套接字 API。安装的 OpenSSL 版本也可能导致行为差异。例如,TLSv1.3 随 OpenSSL 1.1.1 版本一起提供。

警告

在未阅读 安全注意事项 的情况下,请勿使用此模块。这样做可能会导致错误的安全感,因为 ssl 模块的默认设置不一定适合您的应用程序。

可用性:不适用于 Emscripten,不适用于 WASI。

此模块在 WebAssembly 平台 wasm32-emscriptenwasm32-wasi 上不起作用或不可用。有关更多信息,请参阅 WebAssembly 平台

本节介绍 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。

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

侦听本地主机 IPv4 的服务器套接字示例

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

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

上下文创建

一个便捷函数有助于为常见目的创建 SSLContext 对象。

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

返回一个新的 SSLContext 对象,该对象具有针对给定 purpose 的默认设置。这些设置由 ssl 模块选择,并且通常表示比直接调用 SSLContext 构造函数更高的安全级别。

cafilecapathcadata 表示用于证书验证的可信可选 CA 证书,如 SSLContext.load_verify_locations() 中所示。如果所有三个参数均为 None,则此函数可以选择信任系统的默认 CA 证书。

设置如下:PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVEROP_NO_SSLv2OP_NO_SSLv3,以及使用高强度加密密码套件,不使用 RC4 和未经身份验证的密码套件。传递 SERVER_AUTH 作为 purpose 会将 verify_mode 设置为 CERT_REQUIRED,并加载 CA 证书(当至少提供 cafilecapathcadata 之一)或使用 SSLContext.load_default_certs() 加载默认 CA 证书。

如果支持 keylog_filename 并且设置了环境变量 SSLKEYLOGFILE,则 create_default_context() 会启用密钥日志记录。

注意

协议、选项、密码和其他设置可能会随时更改为更严格的值,恕不另行通知。这些值代表了兼容性和安全性之间的良好平衡。

如果您的应用程序需要特定设置,则应创建 SSLContext 并自行应用设置。

注意

如果您发现某些较旧的客户端或服务器尝试使用此函数创建的 SSLContext 进行连接时,收到错误消息“协议或密码套件不匹配”,则可能是它们仅支持此函数使用 OP_NO_SSLv3 排除的 SSL3.0。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。

3.8 版更改: 添加了对 SSLKEYLOGFILE 的密钥日志记录支持。

3.10 版更改: 上下文现在使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 协议,而不是通用的 PROTOCOL_TLS

异常

异常 ssl.SSLError

引发此异常以指示底层 SSL 实现(当前由 OpenSSL 库提供)中的错误。这表示叠加在底层网络连接上的更高级别加密和身份验证层中存在问题。此错误是 OSError 的子类型。SSLError 实例的错误代码和消息由 OpenSSL 库提供。

3.3 版更改: SSLError 曾经是 socket.error 的子类型。

library

一个字符串助记符,用于指定发生错误的 OpenSSL 子模块,例如 SSLPEMX509。可能值的范围取决于 OpenSSL 版本。

3.3 版新增。

reason

一个字符串助记符,用于指定发生此错误的原因,例如 CERTIFICATE_VERIFY_FAILED。可能值的范围取决于 OpenSSL 版本。

3.3 版新增。

异常 ssl.SSLZeroReturnError

SSLError 的子类,在尝试读取或写入数据时引发,表示 SSL 连接已正常关闭。请注意,这并不意味着底层传输(读取 TCP)已关闭。

3.3 版新增。

异常 ssl.SSLWantReadError

SSLError 的子类,由 非阻塞 SSL 套接字 在尝试读取或写入数据时引发,表示在请求能够完成之前,需要在底层 TCP 传输上接收更多数据。

3.3 版新增。

异常 ssl.SSLWantWriteError

SSLError 的子类,由 非阻塞 SSL 套接字 在尝试读取或写入数据时引发,表示在请求能够完成之前,需要在底层 TCP 传输上发送更多数据。

3.3 版新增。

异常 ssl.SSLSyscallError

SSLError 的子类,在尝试对 SSL 套接字执行操作时遇到系统错误时引发。遗憾的是,没有简单的方法可以检查原始 errno 号。

3.3 版新增。

异常 ssl.SSLEOFError

SSLError 的子类,在 SSL 连接突然终止时引发。通常,遇到此错误时,您不应该尝试重用底层传输。

3.3 版新增。

异常 ssl.SSLCertVerificationError

SSLError 的子类,在证书验证失败时引发。

3.7 版新增。

verify_code

表示验证错误的数字错误号。

verify_message

验证错误的人类可读字符串。

exception ssl.CertificateError

SSLCertVerificationError 的别名。

在 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 版更改: 现在接受可写的 类字节对象

证书处理

ssl.cert_time_to_seconds(cert_time)

以秒为单位返回自 Epoch 以来的时间,给定 cert_time 字符串,该字符串表示证书中以 "%b %d %H:%M:%S %Y %Z" strptime 格式(C 区域设置)表示的“notBefore”或“notAfter”日期。

下面是一个例子

>>> 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,作为 (主机名, 端口号) 对,获取服务器的证书,并将其作为 PEM 编码的字符串返回。如果指定了 ssl_version,则使用该版本的 SSL 协议尝试连接到服务器。如果指定了 ca_certs,则它应该是一个包含根证书列表的文件,其格式与 SSLContext.load_verify_locations()cafile 参数所使用的格式相同。该调用将尝试根据该组根证书验证服务器证书,如果验证尝试失败,则会失败。可以使用 timeout 参数指定超时。

在 3.3 版更改: 此函数现在兼容 IPv6。

在 3.5 版更改: 默认的 ssl_versionPROTOCOL_SSLv3 更改为 PROTOCOL_TLS,以便与现代服务器实现最大兼容性。

在 3.10 版更改: 添加了 timeout 参数。

ssl.DER_cert_to_PEM_cert(DER_cert_bytes)

给定一个证书作为 DER 编码的字节块,返回相同证书的 PEM 编码字符串版本。

ssl.PEM_cert_to_DER_cert(PEM_cert_string)

给定一个证书作为 ASCII PEM 字符串,返回相同证书的 DER 编码字节序列。

ssl.get_default_verify_paths()

返回一个命名元组,其中包含 OpenSSL 默认 cafile 和 capath 的路径。这些路径与 SSLContext.set_default_verify_paths() 所使用的路径相同。返回值是一个 命名元组 DefaultVerifyPaths

  • cafile - cafile 的解析路径,如果文件不存在,则为 None

  • capath - capath 的解析路径,如果目录不存在,则为 None

  • openssl_cafile_env - 指向 cafile 的 OpenSSL 环境变量键,

  • openssl_cafile - cafile 的硬编码路径,

  • openssl_capath_env - 指向 capath 的 OpenSSL 环境变量键,

  • openssl_capath - capath 目录的硬编码路径

3.4 版新增。

ssl.enum_certificates(store_name)

从 Windows 的系统证书存储区检索证书。store_name 可以是 CAROOTMY 之一。Windows 也可能提供其他证书存储区。

该函数返回一个 (cert_bytes, encoding_type, trust) 元组列表。encoding_type 指定 cert_bytes 的编码。它可以是 x509_asn(表示 X.509 ASN.1 数据)或 pkcs_7_asn(表示 PKCS#7 ASN.1 数据)。Trust 指定证书的用途,它可以是一组 OID,如果证书对所有用途都是可信的,则为 True

示例

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

可用性:Windows。

3.4 版新增。

ssl.enum_crls(store_name)

从 Windows 的系统证书存储区检索 CRL。store_name 可以是 CAROOTMY 之一。Windows 也可能提供其他证书存储区。

该函数返回一个 (cert_bytes, encoding_type, trust) 元组列表。encoding_type 指定 cert_bytes 的编码。它可以是 x509_asn(表示 X.509 ASN.1 数据)或 pkcs_7_asn(表示 PKCS#7 ASN.1 数据)。

可用性:Windows。

3.4 版新增。

常量

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

版本 3.6 中的新功能。

ssl.CERT_NONE

SSLContext.verify_mode 的可能值。除 PROTOCOL_TLS_CLIENT 外,这是默认模式。对于客户端套接字,几乎接受任何证书。验证错误(例如不受信任或过期的证书)将被忽略,并且不会中止 TLS/SSL 握手。

在服务器模式下,不会向客户端请求证书,因此客户端不会发送任何用于客户端证书身份验证的证书。

请参阅下文对 安全注意事项 的讨论。

ssl.CERT_OPTIONAL

SSLContext.verify_mode 的可能值。在客户端模式下,CERT_OPTIONALCERT_REQUIRED 含义相同。建议对客户端套接字使用 CERT_REQUIRED

在服务器模式下,会向客户端发送客户端证书请求。客户端可以忽略该请求,也可以发送证书以执行 TLS 客户端证书身份验证。如果客户端选择发送证书,则会对其进行验证。任何验证错误都会立即中止 TLS 握手。

使用此设置需要将一组有效的 CA 证书传递给 SSLContext.load_verify_locations()

ssl.CERT_REQUIRED

SSLContext.verify_mode 的可能值。在此模式下,需要套接字连接的另一端提供证书;如果未提供证书或证书验证失败,则会引发 SSLError。此模式不足以验证客户端模式下的证书,因为它不匹配主机名。还必须启用 check_hostname 才能验证证书的真实性。PROTOCOL_TLS_CLIENT 默认使用 CERT_REQUIRED 并启用 check_hostname

对于服务器套接字,此模式提供强制性 TLS 客户端证书身份验证。会向客户端发送客户端证书请求,并且客户端必须提供有效且受信任的证书。

使用此设置需要将一组有效的 CA 证书传递给 SSLContext.load_verify_locations()

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。

3.10 版新增。

class ssl.VerifyFlags

VERIFY_* 常量的 enum.IntFlag 集合。

版本 3.6 中的新功能。

ssl.PROTOCOL_TLS

选择客户端和服务器都支持的最高协议版本。尽管名称如此,但此选项可以选择“SSL”和“TLS”协议。

版本 3.6 中的新功能。

3.10 版后已弃用: TLS 客户端和服务器需要不同的默认设置才能进行安全通信。不推荐使用通用的 TLS 协议常量,而推荐使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER

ssl.PROTOCOL_TLS_CLIENT

自动协商客户端和服务器都支持的最高协议版本,并配置上下文客户端连接。该协议默认启用 CERT_REQUIREDcheck_hostname

版本 3.6 中的新功能。

ssl.PROTOCOL_TLS_SERVER

自动协商客户端和服务器都支持的最高协议版本,并配置上下文服务器端连接。

版本 3.6 中的新功能。

ssl.PROTOCOL_SSLv23

PROTOCOL_TLS 的别名。

3.6 版后已弃用: 请改用 PROTOCOL_TLS

ssl.PROTOCOL_SSLv3

选择 SSL 版本 3 作为通道加密协议。

如果 OpenSSL 是使用 no-ssl3 选项编译的,则此协议不可用。

警告

SSL 版本 3 不安全。强烈建议不要使用它。

3.6 版后已弃用: OpenSSL 已弃用所有特定于版本的协议。请改用默认协议 PROTOCOL_TLS_SERVERPROTOCOL_TLS_CLIENT 以及 SSLContext.minimum_versionSSLContext.maximum_version

ssl.PROTOCOL_TLSv1

选择 TLS 版本 1.0 作为通道加密协议。

3.6 版后已弃用: OpenSSL 已弃用所有特定于版本的协议。

ssl.PROTOCOL_TLSv1_1

选择 TLS 版本 1.1 作为通道加密协议。仅适用于 openssl 1.0.1+ 版本。

3.4 版新增。

3.6 版后已弃用: OpenSSL 已弃用所有特定于版本的协议。

ssl.PROTOCOL_TLSv1_2

选择 TLS 版本 1.2 作为通道加密协议。仅适用于 openssl 1.0.1+ 版本。

3.4 版新增。

3.6 版后已弃用: OpenSSL 已弃用所有特定于版本的协议。

ssl.OP_ALL

启用对其他 SSL 实现中存在的各种错误的解决方法。默认情况下设置此选项。它不一定设置与 OpenSSL 的 SSL_OP_ALL 常量相同的标志。

3.2 版新增。

ssl.OP_NO_SSLv2

阻止 SSLv2 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用。它可以防止对等方选择 SSLv2 作为协议版本。

3.2 版新增。

3.6 版后已弃用: SSLv2 已弃用

ssl.OP_NO_SSLv3

阻止 SSLv3 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用。它可以防止对等方选择 SSLv3 作为协议版本。

3.2 版新增。

3.6 版后已弃用: SSLv3 已弃用

ssl.OP_NO_TLSv1

阻止 TLSv1 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用。它可以防止对等方选择 TLSv1 作为协议版本。

3.2 版新增。

3.7 版后已弃用: 自 OpenSSL 1.1.0 起弃用此选项,请改用新的 SSLContext.minimum_versionSSLContext.maximum_version

ssl.OP_NO_TLSv1_1

阻止 TLSv1.1 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用的情况。它可以防止对等方选择 TLSv1.1 作为协议版本。仅适用于 openssl 1.0.1+ 版本。

3.4 版新增。

3.7 版后已弃用: 自 OpenSSL 1.1.0 起已弃用此选项。

ssl.OP_NO_TLSv1_2

阻止 TLSv1.2 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用的情况。它可以防止对等方选择 TLSv1.2 作为协议版本。仅适用于 openssl 1.0.1+ 版本。

3.4 版新增。

3.7 版后已弃用: 自 OpenSSL 1.1.0 起已弃用此选项。

ssl.OP_NO_TLSv1_3

阻止 TLSv1.3 连接。此选项仅适用于与 PROTOCOL_TLS 结合使用的情况。它可以防止对等方选择 TLSv1.3 作为协议版本。TLS 1.3 适用于 OpenSSL 1.1.1 或更高版本。如果 Python 是针对较旧版本的 OpenSSL 编译的,则该标志默认为 *0*。

3.6.3 版后添加。

3.7 版后已弃用: 自 OpenSSL 1.1.0 起已弃用此选项。为了向后兼容 OpenSSL 1.0.2,已将其添加到 2.7.15 和 3.6.3 中。

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 通道上的压缩。如果应用程序协议支持其自身的压缩方案,则此选项很有用。

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 提供程序执行。如果应用程序要求所有加密操作都由 FIPS 提供程序执行,则这可能是不受欢迎的。

此选项仅适用于 OpenSSL 3.0.0 及更高版本。

3.12 版后添加。

ssl.OP_LEGACY_SERVER_CONNECT

仅允许 OpenSSL 与未修补的服务器之间进行旧版不安全的重新协商。

3.12 版后添加。

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 库是否内置支持基于椭圆曲线的迪菲-赫尔曼密钥交换。除非发行版明确禁用了此功能,否则应为 true。

3.3 版新增。

ssl.HAS_SNI

OpenSSL 库是否内置支持 *服务器名称指示* 扩展(如 RFC 6066 中所定义)。

3.2 版新增。

ssl.HAS_NPN

OpenSSL 库是否内置支持 应用程序层协议协商 中所述的 *下一协议协商*。如果为 true,则可以使用 SSLContext.set_npn_protocols() 方法来公布要支持的协议。

3.3 版新增。

ssl.HAS_SSLv2

OpenSSL 库是否内置支持 SSL 2.0 协议。

3.7 版新增。

ssl.HAS_SSLv3

OpenSSL 库是否内置支持 SSL 3.0 协议。

3.7 版新增。

ssl.HAS_TLSv1

OpenSSL 库是否内置支持 TLS 1.0 协议。

3.7 版新增。

ssl.HAS_TLSv1_1

OpenSSL 库是否内置支持 TLS 1.1 协议。

3.7 版新增。

ssl.HAS_TLSv1_2

OpenSSL 库是否内置支持 TLS 1.2 协议。

3.7 版新增。

ssl.HAS_TLSv1_3

OpenSSL 库是否内置支持 TLS 1.3 协议。

3.7 版新增。

ssl.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 中的新功能。

用途。SERVER_AUTH

create_default_context()SSLContext.load_default_certs() 的选项。此值表示该上下文可用于对 Web 服务器进行身份验证(因此,它将用于创建客户端套接字)。

3.4 版新增。

用途。CLIENT_AUTH

create_default_context()SSLContext.load_default_certs() 的选项。此值表示该上下文可用于对 Web 客户端进行身份验证(因此,它将用于创建服务器端套接字)。

3.4 版新增。

class ssl.SSLErrorNumber

enum.IntEnum SSL_ERROR_* 常量集合。

版本 3.6 中的新功能。

class ssl.TLSVersion

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

3.7 版新增。

TLSVersion.MINIMUM_SUPPORTED
TLSVersion.MAXIMUM_SUPPORTED

支持的最低或最高 SSL 或 TLS 版本。这些是魔术常量。它们的值并不反映最低和最高的可用 TLS/SSL 版本。

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

SSL 3.0 到 TLS 1.3。

自 3.10 版起已弃用: TLSVersion.TLSv1_2TLSVersion.TLSv1_3 之外的所有 TLSVersion 成员均已弃用。

SSL 套接字

class ssl.SSLSocket(socket.socket)

SSL 套接字提供以下 套接字对象 方法

但是,由于 SSL(和 TLS)协议在 TCP 之上拥有自己的框架,因此 SSL 套接字抽象在某些方面可能与正常的操作系统级套接字规范有所不同。特别参见关于非阻塞套接字的说明

SSLSocket 的实例必须使用 SSLContext.wrap_socket() 方法创建。

版本 3.5 中的变化: 添加了 sendfile() 方法。

版本 3.5 中的变化: shutdown() 不会在每次接收或发送字节时重置套接字超时。套接字超时现在是关闭的最大总持续时间。

版本 3.6 中弃用: 不建议直接创建 SSLSocket 实例,请使用 SSLContext.wrap_socket() 来包装套接字。

版本 3.7 中的变化: SSLSocket 实例必须使用 wrap_socket() 创建。在早期版本中,可以直接创建实例。这从未被记录或官方支持。

版本 3.10 中的变化: Python 现在在内部使用 SSL_read_exSSL_write_ex。这些函数支持读取和写入大于 2 GB 的数据。写入零长度数据不再因协议违规错误而失败。

SSL 套接字还具有以下附加方法和属性

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

从 SSL 套接字读取最多 len 字节的数据,并将结果作为 bytes 实例返回。如果指定了 buffer,则读入缓冲区,并返回读取的字节数。

如果套接字是非阻塞的并且读取操作将阻塞,则引发 SSLWantReadErrorSSLWantWriteError

由于可以随时进行重新协商,因此调用 read() 也可能导致写入操作。

版本 3.5 中的变化: 套接字超时不再在每次接收或发送字节时重置。套接字超时现在是读取最多 len 字节的最大总持续时间。

版本 3.6 中弃用: 使用 recv() 代替 read()

SSLSocket.write(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 属性为 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 实例。如果证书未经验证,则该字典为空。如果证书已通过验证,则它将返回一个包含多个键的字典,其中包括 subject(证书颁发给的主体)和 issuer(颁发证书的主体)。如果证书包含 *Subject Alternative Name* 扩展的实例(请参阅 RFC 3280),则字典中还将包含 subjectAltName 键。

subjectissuer 字段是元组,其中包含证书数据结构中为相应字段提供的相对专有名称 (RDN) 序列,每个 RDN 是一个名称-值对序列。以下是一个现实示例

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

如果 binary_form 参数为 True,并且提供了证书,则此方法以字节序列的形式返回整个证书的 DER 编码形式,如果对等方未提供证书,则返回 None。对等方是否提供证书取决于 SSL 套接字的角色

  • 对于客户端 SSL 套接字,服务器将始终提供证书,而无论是否需要验证;

  • 对于服务器 SSL 套接字,客户端仅在服务器请求时才提供证书;因此,如果您使用的是 CERT_NONE(而不是 CERT_OPTIONALCERT_REQUIRED),则 getpeercert() 将返回 None

另请参阅 SSLContext.check_hostname

版本 3.2 中的变化: 返回的字典包含其他项,例如 issuernotBefore

版本 3.4 中的变化: 如果未完成握手,则会引发 ValueError。返回的字典包含其他 X509v3 扩展项,例如 crlDistributionPointscaIssuersOCSP URI。

版本 3.9 中的变化: IPv6 地址字符串不再有尾随换行符。

SSLSocket.cipher()

返回一个包含三个值的三元组,其中包含正在使用的密码的名称、定义其用途的 SSL 协议版本以及正在使用的密钥位数。如果尚未建立连接,则返回 None

SSLSocket.shared_ciphers()

返回客户端和服务器都可用的密码列表。返回列表的每个条目都是一个包含三个值的三元组,其中包含密码的名称、定义其用途的 SSL 协议版本以及密码使用的密钥位数。如果尚未建立连接或套接字是客户端套接字,则 shared_ciphers() 返回 None

3.5 版后添加。

SSLSocket.compression()

以字符串形式返回正在使用的压缩算法,如果连接未压缩,则返回 None

如果更高级别的协议支持其自身的压缩机制,则可以使用 OP_NO_COMPRESSION 禁用 SSL 级别的压缩。

3.3 版新增。

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

获取当前连接的通道绑定数据,作为字节对象。如果未连接或握手尚未完成,则返回 None

cb_type 参数允许选择所需的通道绑定类型。有效的通道绑定类型列在 CHANNEL_BINDING_TYPES 列表中。目前仅支持 RFC 5929 定义的“tls-unique”通道绑定。如果请求了不受支持的通道绑定类型,则会引发 ValueError

3.3 版新增。

SSLSocket.selected_alpn_protocol()

返回在 TLS 握手期间选择的协议。如果未调用 SSLContext.set_alpn_protocols(),如果对方不支持 ALPN,如果此套接字不支持客户端提出的任何协议,或者如果握手尚未发生,则返回 None

3.5 版后添加。

SSLSocket.selected_npn_protocol()

返回在 TLS/SSL 握手期间选择的高层协议。如果未调用 SSLContext.set_npn_protocols(),或者如果对方不支持 NPN,或者如果握手尚未发生,则此方法将返回 None

3.3 版新增。

自版本 3.10 起弃用: NPN 已被 ALPN 取代

SSLSocket.unwrap()

执行 SSL 关闭握手,它从底层套接字中移除 TLS 层,并返回底层套接字对象。这可以用于从连接上的加密操作转换为未加密操作。返回的套接字应始终用于与连接另一端的进一步通信,而不是原始套接字。

SSLSocket.verify_client_post_handshake()

从 TLS 1.3 客户端请求握手后身份验证 (PHA)。PHA 只能从服务器端套接字启动 TLS 1.3 连接,在初始 TLS 握手之后并在双方都启用了 PHA 的情况下,请参阅 SSLContext.post_handshake_auth

该方法不会立即执行证书交换。服务器端在下一个写入事件期间发送 CertificateRequest,并期望客户端在下一个读取事件上使用证书进行响应。

如果未满足任何先决条件(例如,不是 TLS 1.3,未启用 PHA),则会引发 SSLError

注意

仅适用于 OpenSSL 1.1.1 及更高版本且启用了 TLS 1.3。如果不支持 TLS 1.3,则该方法会引发 NotImplementedError

3.8 版后添加。

SSLSocket.version()

以字符串形式返回由连接协商的实际 SSL 协议版本,如果未建立安全连接,则返回 None。在撰写本文时,可能的返回值包括 "SSLv2""SSLv3""TLSv1""TLSv1.1""TLSv1.2"。最近的 OpenSSL 版本可能会定义更多返回值。

3.5 版后添加。

SSLSocket.pending()

返回连接上可供读取的已解密字节数。

SSLSocket.context

此 SSL 套接字绑定到的 SSLContext 对象。

3.2 版新增。

SSLSocket.server_side

一个布尔值,对于服务器端套接字为 True,对于客户端套接字为 False

3.2 版新增。

SSLSocket.server_hostname

服务器的主机名:str 类型,对于服务器端套接字或如果在构造函数中未指定主机名,则为 None

3.2 版新增。

在 3.7 版更改: 该属性现在始终为 ASCII 文本。当 server_hostname 是国际化域名 (IDN) 时,此属性现在存储 A-label 形式("xn--pythn-mua.org"),而不是 U-label 形式("pythön.org")。

SSLSocket.session

此 SSL 连接的 SSLSession。在执行 TLS 握手后,客户端和服务器端套接字可以使用该会话。对于客户端套接字,可以在调用 do_handshake() 之前设置会话以重用会话。

版本 3.6 中的新功能。

SSLSocket.session_reused

版本 3.6 中的新功能。

SSL 上下文

3.2 版新增。

SSL 上下文保存着比单个 SSL 连接更持久化的各种数据,例如 SSL 配置选项、证书和私钥。它还管理服务器端套接字的 SSL 会话缓存,以便加快来自同一客户端的重复连接。

class ssl.SSLContext(protocol=None)

创建一个新的 SSL 上下文。您可以传递 protocol,它必须是此模块中定义的 PROTOCOL_* 常量之一。该参数指定要使用的 SSL 协议版本。通常,服务器选择特定的协议版本,客户端必须适应服务器的选择。大多数版本与其他版本不兼容。如果未指定,则默认为 PROTOCOL_TLS;它提供了与其他版本的最佳兼容性。

下表显示了客户端(侧面)中的哪些版本可以连接到服务器(顶部)中的哪些版本

客户端 / 服务器

SSLv2

SSLv3

TLS [3]

TLSv1

TLSv1.1

TLSv1.2

SSLv2

[1]

SSLv3

[2]

TLS (SSLv23) [3]

[1]

[2]

TLSv1

TLSv1.1

TLSv1.2

脚注

另请参阅

create_default_context()ssl 模块为给定目的选择安全设置。

版本 3.6 中的变化: 上下文将使用安全的默认值创建。默认情况下会设置选项 OP_NO_COMPRESSIONOP_CIPHER_SERVER_PREFERENCEOP_SINGLE_DH_USEOP_SINGLE_ECDH_USEOP_NO_SSLv2OP_NO_SSLv3PROTOCOL_SSLv3 除外)。初始密码套件列表仅包含 HIGH 密码,不包含 NULL 密码和 MD5 密码。

3.10 版后已弃用: 不带 protocol 参数的 SSLContext 已弃用。上下文类将来将需要 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 协议。

版本 3.10 中的变化: 默认密码套件现在仅包含具有前向保密性和安全级别 2 的安全 AES 和 ChaCha20 密码。禁止使用小于 2048 位的 RSA 和 DH 密钥以及小于 224 位的 ECC 密钥。PROTOCOL_TLSPROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 使用 TLS 1.2 作为最低 TLS 版本。

注意

一旦被连接使用,SSLContext 仅支持有限的变动。允许向内部信任库添加新证书,但更改密码、验证设置或 mTLS 证书可能会导致意外行为。

注意

SSLContext 旨在供多个连接共享和使用。因此,只要在被连接使用后未重新配置,它就是线程安全的。

SSLContext 对象具有以下方法和属性

SSLContext.cert_store_stats()

获取有关已加载 X.509 证书数量、标记为 CA 证书的 X.509 证书数量和证书吊销列表数量的统计信息,以字典形式返回。

具有一个 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)

加载一组“证书颁发机构”(CA) 证书,当 verify_mode 不是 CERT_NONE 时,使用这些证书来验证其他对等方的证书。必须至少指定 cafilecapath 中的一个。

此方法还可以加载 PEM 或 DER 格式的证书吊销列表 (CRL)。为了使用 CRL,必须正确配置 SSLContext.verify_flags

cafile 字符串(如果存在)是 PEM 格式的连接 CA 证书文件的路径。有关如何在此文件中排列证书的更多信息,请参阅 证书 的讨论。

capath 字符串(如果存在)是指向包含多个 PEM 格式的 CA 证书的目录的路径,遵循 OpenSSL 特定的布局

cadata 对象(如果存在)是一个或多个 PEM 编码证书的 ASCII 字符串或 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

注意

连接后,SSL 套接字的 SSLSocket.cipher() 方法将给出当前选择的密码。

无法使用 set_ciphers() 禁用 TLS 1.3 密码套件。

SSLContext.set_alpn_protocols(protocols)

指定套接字在 SSL/TLS 握手期间应通告的协议。它应该是一个 ASCII 字符串列表,例如 ['http/1.1', 'spdy/2'],按优先级排序。协议的选择将在握手期间进行,并将根据 RFC 7301 进行。握手成功后,SSLSocket.selected_alpn_protocol() 方法将返回协商的协议。

如果 HAS_ALPNFalse,则此方法将引发 NotImplementedError

3.5 版后添加。

SSLContext.set_npn_protocols(protocols)

指定套接字在 SSL/TLS 握手期间应通告的协议。它应该是一个字符串列表,例如 ['http/1.1', 'spdy/2'],按优先级排序。协议的选择将在握手期间进行,并将根据 应用层协议协商 进行。握手成功后,SSLSocket.selected_npn_protocol() 方法将返回协商的协议。

如果 HAS_NPNFalse,则此方法将引发 NotImplementedError

3.3 版新增。

自版本 3.10 起弃用: NPN 已被 ALPN 取代

SSLContext.sni_callback

注册一个回调函数,当 TLS 客户端指定服务器名称指示时,SSL/TLS 服务器在收到 TLS 客户端问候握手消息后将调用该函数。服务器名称指示机制在 RFC 6066 第 3 节 - 服务器名称指示中指定。

每个 SSLContext 只能设置一个回调。如果将 sni_callback 设置为 None,则禁用回调。后续调用此函数将禁用先前注册的回调。

回调函数将使用三个参数调用;第一个是 ssl.SSLSocket,第二个是表示客户端打算通信的服务器名称的字符串(如果 TLS 客户端问候不包含服务器名称,则为 None),第三个参数是原始的 SSLContext。服务器名称参数是文本。对于国际化域名,服务器名称是 IDN A 标签("xn--pythn-mua.org")。

此回调的典型用法是将 ssl.SSLSocketSSLSocket.context 属性更改为类型为 SSLContext 的新对象,该对象表示与服务器名称匹配的证书链。

由于 TLS 连接处于早期协商阶段,因此只有有限的方法和属性可用,例如 SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert()SSLSocket.cipher()SSLSocket.compression() 方法要求 TLS 连接已超出 TLS 客户端问候阶段,因此不会返回有意义的值,也不能安全地调用它们。

sni_callback 函数必须返回 None 以允许 TLS 协商继续。如果需要 TLS 失败,则可以返回常量 ALERT_DESCRIPTION_*。其他返回值将导致 TLS 致命错误,并显示 ALERT_DESCRIPTION_INTERNAL_ERROR

如果从 sni_callback 函数引发异常,则 TLS 连接将终止,并向客户端发送致命 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_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 为 true,则指定 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

要将一个 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。可以在类的实例上覆盖该属性,以便返回 SSLSocket 的自定义子类。

3.7 版新增。

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

包装 BIO 对象 incomingoutgoing 并返回 SSLContext.sslobject_class 的实例(默认为 SSLObject)。SSL 例程将从传入的 BIO 读取输入数据,并将数据写入传出的 BIO。

server_sideserver_hostnamesession 参数的含义与 SSLContext.wrap_socket() 中的相同。

版本 3.6 中的变化: 添加了 session 参数。

版本 3.7 中的变化: 该方法返回 SSLContext.sslobject_class 的实例,而不是硬编码的 SSLObject

SSLContext.sslobject_class

SSLContext.wrap_bio() 的返回类型,默认为 SSLObject。可以在类的实例上覆盖该属性,以便返回 SSLObject 的自定义子类。

3.7 版新增。

SSLContext.session_stats()

获取有关由此上下文创建或管理的 SSL 会话的统计信息。返回一个字典,该字典将每个 信息片段 的名称映射到其数值。例如,以下是自创建上下文以来会话缓存中的命中总数和未命中总数

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

是否在 SSLSocket.do_handshake() 中匹配对等证书的主机名。上下文的 verify_mode 必须设置为 CERT_OPTIONALCERT_REQUIRED,并且您必须将 server_hostname 传递给 wrap_socket() 才能匹配主机名。启用主机名检查会自动将 verify_modeCERT_NONE 设置为 CERT_REQUIRED。只要启用了主机名检查,就不能将其设置回 CERT_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_NONE 时,verify_mode 会自动更改为 CERT_REQUIRED。以前,相同的操作会因 ValueError 而失败。

SSLContext.keylog_filename

每当生成或接收到密钥材料时,将 TLS 密钥写入密钥日志文件。密钥日志文件仅用于调试目的。文件格式由 NSS 指定,并由许多流量分析器(如 Wireshark)使用。日志文件以仅追加模式打开。写入在不同线程之间同步,但在不同进程之间不同步。

3.8 版后添加。

SSLContext.maximum_version

一个 TLSVersion 枚举成员,表示支持的最高 TLS 版本。默认值为 TLSVersion.MAXIMUM_SUPPORTED。对于 PROTOCOL_TLSPROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 以外的协议,该属性为只读。

属性 maximum_versionminimum_versionSSLContext.options 都会影响上下文中支持的 SSL 和 TLS 版本。该实现不阻止无效组合。例如,一个 options 中包含 OP_NO_TLSv1_2maximum_version 设置为 TLSVersion.TLSv1_2 的上下文将无法建立 TLS 1.2 连接。

3.7 版新增。

SSLContext.minimum_version

SSLContext.maximum_version 类似,但它是支持的最低版本或 TLSVersion.MINIMUM_SUPPORTED

3.7 版新增。

SSLContext.num_tickets

控制 PROTOCOL_TLS_SERVER 上下文的 TLS 1.3 会话票证数量。该设置对 TLS 1.0 到 1.2 连接没有影响。

3.8 版后添加。

SSLContext.options

一个整数,表示在此上下文中启用的 SSL 选项集。默认值为 OP_ALL,但您可以通过将其他选项(如 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 版后已弃用: 自 Python 3.7 起,所有 OP_NO_SSL*OP_NO_TLS* 选项均已弃用。请改用 SSLContext.minimum_versionSSLContext.maximum_version

SSLContext.post_handshake_auth

启用 TLS 1.3 握手后客户端身份验证。默认情况下,握手后身份验证处于禁用状态,服务器只能在初始握手中请求 TLS 客户端证书。启用后,服务器可以在握手后的任何时间请求 TLS 客户端证书。

在客户端套接字上启用后,客户端会向服务器发送信号,表明它支持握手后身份验证。

在服务器端套接字上启用后,SSLContext.verify_mode 也必须设置为 CERT_OPTIONALCERT_REQUIRED。实际的客户端证书交换将延迟到调用 SSLSocket.verify_client_post_handshake() 并执行一些 I/O 操作之后。

3.8 版后添加。

SSLContext.protocol

构造上下文时选择的协议版本。此属性为只读。

SSLContext.hostname_checks_common_name

在没有主题备用名称扩展的情况下,check_hostname 是否回退以验证证书的主题通用名称(默认值:true)。

3.7 版新增。

在 3.10 版更改: 在 1.1.1l 之前的 OpenSSL 版本中,该标志无效。Python 3.8.9、3.9.3 和 3.10 包含针对先前版本的解决方法。

SSLContext.security_level

表示上下文 安全级别 的整数。此属性为只读。

3.10 版新增。

SSLContext.verify_flags

证书验证操作的标志。您可以通过将标志(如 VERIFY_CRL_CHECK_LEAF)进行“或”运算来设置它们。默认情况下,OpenSSL 既不要求也不验证证书吊销列表 (CRL)。

3.4 版新增。

在 3.6 版更改: SSLContext.verify_flags 返回 VerifyFlags 标志

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

是否尝试验证其他对等方的证书,以及如果验证失败该如何操作。此属性必须是 CERT_NONECERT_OPTIONALCERT_REQUIRED 之一。

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

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

证书

证书通常是公钥/私钥系统的一部分。在这个系统中,每个*主体*(可以是机器、个人或组织)都被分配了一个唯一的两部分加密密钥。密钥的一部分是公开的,称为*公钥*;另一部分是保密的,称为*私钥*。这两部分是相关的,因为如果您使用其中一部分加密消息,则可以使用另一部分解密它,并且**只能**使用另一部分。

证书包含有关两个主体的信息。它包含*主题*的名称和主题的公钥。它还包含第二个主体(*颁发者*)的声明,即主题是他们声称的人,并且这确实是主题的公钥。颁发者的声明使用颁发者的私钥签名,只有颁发者知道。但是,任何人都可以通过找到颁发者的公钥、使用它解密声明并将其与证书中的其他信息进行比较来验证颁发者的声明。证书还包含有关其有效期的时间段信息。这表示为两个字段,称为“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()。如果私钥与证书一起存储,则它应该位于证书链中第一个证书之前

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

自签名证书

如果您要创建一个提供 SSL 加密连接服务的服务器,则需要获取该服务的证书。获取适当证书的方法有很多,例如从证书颁发机构购买证书。另一种常见做法是生成自签名证书。最简单的方法是使用 OpenSSL 包,使用如下所示的内容

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

自签名证书的缺点是它是自己的根证书,其他任何人的已知(和受信任)根证书缓存中都没有它。

示例

测试 SSL 支持

要测试 Python 安装中是否存在 SSL 支持,用户代码应使用以下习惯用法

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

客户端操作

此示例使用推荐的客户端套接字安全设置(包括自动证书验证)创建 SSL 上下文

>>> context = ssl.create_default_context()

如果您更喜欢自己调整安全设置,则可以从头开始创建上下文(但请注意,您可能无法正确设置)

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

(此代码段假设您的操作系统将所有 CA 证书的捆绑包放在 /etc/ssl/certs/ca-bundle.crt 中;如果不是,您将收到错误消息,并且必须调整位置)

PROTOCOL_TLS_CLIENT 协议配置用于证书验证和主机名验证的上下文。verify_mode 设置为 CERT_REQUIREDcheck_hostname 设置为 True。所有其他协议都使用不安全的默认值创建 SSL 上下文。

当您使用上下文连接到服务器时,CERT_REQUIREDcheck_hostname 会验证服务器证书:它确保服务器证书是用其中一个 CA 证书签名的,检查签名的正确性,并验证其他属性,如有效性和主机名的身份

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

然后,您可以获取证书

>>> cert = conn.getpeercert()

目视检查表明该证书确实标识了所需的服务(即 HTTPS 主机 www.python.org

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

现在 SSL 通道已建立并验证了证书,您可以继续与服务器通信

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

请参阅下文对 安全注意事项 的讨论。

服务器端操作

对于服务器操作,通常需要服务器证书和私钥,每个文件一个。首先要创建一个包含密钥和证书的上下文,以便客户端可以检查您的身份验证。然后打开一个套接字,将其绑定到一个端口,在其上调用 listen(),并开始等待客户端连接。

import socket, ssl

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

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

当客户端连接时,您将在套接字上调用 accept() 以从另一端获取新的套接字,并使用上下文的 SSLContext.wrap_socket() 方法为连接创建服务器端 SSL 套接字。

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

然后,您将从 connstream 读取数据并对其进行处理,直到您完成与客户端的交互(或客户端完成与您的交互)。

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

然后返回监听新的客户端连接(当然,真正的服务器可能会在单独的线程中处理每个客户端连接,或者将套接字置于 非阻塞模式 并使用事件循环)。

关于非阻塞套接字的说明

SSL 套接字在非阻塞模式下的行为与常规套接字略有不同。因此,在使用非阻塞套接字时,需要注意以下几点。

  • 大多数 SSLSocket 方法会引发 SSLWantWriteErrorSSLWantReadError,而不是 BlockingIOError,如果 I/O 操作会被阻塞。如果需要对底层套接字进行读取操作,则会引发 SSLWantReadError,如果需要对底层套接字进行写入操作,则会引发 SSLWantWriteError。请注意,尝试向 SSL 套接字进行*写入*操作可能需要先从底层套接字进行*读取*操作,而尝试从 SSL 套接字进行*读取*操作可能需要先向底层套接字进行*写入*操作。

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

  • 调用 select() 会告诉您可以从操作系统级套接字读取数据(或写入数据),但这并不意味着上层 SSL 层有足够的数据。例如,可能只收到了部分 SSL 帧。因此,您必须准备好处理 SSLSocket.recv()SSLSocket.send() 失败,并在再次调用 select() 后重试。

  • 相反,由于 SSL 层有自己的帧结构,因此 SSL 套接字可能仍然有可供读取的数据,而 select() 并不知道。因此,您应该首先调用 SSLSocket.recv() 来清空任何可能可用的数据,然后仅在仍然需要时才阻塞 select() 调用。

    (当然,在使用其他原语时,例如 poll()selectors 模块中的原语时,也适用类似的规定)

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

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

另请参阅

asyncio 模块支持 非阻塞 SSL 套接字,并提供更高级别的 API。它使用 selectors 模块轮询事件,并处理 SSLWantWriteErrorSSLWantReadErrorBlockingIOError 异常。它也异步运行 SSL 握手。

内存 BIO 支持

3.5 版后添加。

自 Python 2.6 中引入 SSL 模块以来,SSLSocket 类提供了两个相关但不同的功能领域。

  • SSL 协议处理

  • 网络 IO

网络 IO API 与 socket.socket 提供的 API 相同,SSLSocket 也继承自该 API。这允许将 SSL 套接字用作常规套接字的直接替代品,从而可以非常轻松地将 SSL 支持添加到现有应用程序中。

结合 SSL 协议处理和网络 IO 通常效果很好,但在某些情况下则不然。例如,异步 IO 框架希望使用与 socket.socket 和内部 OpenSSL 套接字 IO 例程所假设的“在文件描述符上选择/轮询”(基于就绪状态)模型不同的 IO 多路复用模型。这主要与 Windows 等平台相关,因为在这些平台上,此模型效率不高。为此,提供了一个名为 SSLObjectSSLSocket 的简化范围变体。

class ssl.SSLObject

SSLSocket 的简化范围变体,表示不包含任何网络 IO 方法的 SSL 协议实例。此类通常由希望通过内存缓冲区为 SSL 实现异步 IO 的框架作者使用。

此类在 OpenSSL 实现的低级 SSL 对象之上实现了一个接口。此对象捕获 SSL 连接的状态,但不提供任何网络 IO 本身。IO 需要通过单独的“BIO”对象执行,这些对象是 OpenSSL 的 IO 抽象层。

此类没有公共构造函数。必须使用 wrap_bio() 方法创建 SSLObject 实例。此方法将创建 SSLObject 实例并将其绑定到一对 BIO。*传入* BIO 用于将数据从 Python 传递到 SSL 协议实例,而*传出* BIO 用于以相反的方式传递数据。

以下方法可用:

SSLSocket 相比,此对象缺少以下功能

  • 任何形式的网络 IO;recv()send() 仅对底层 MemoryBIO 缓冲区进行读写。

  • 没有 do_handshake_on_connect 机制。您必须始终手动调用 do_handshake() 来启动握手。

  • 没有对 suppress_ragged_eofs 的处理。所有违反协议的文件结尾条件都将通过 SSLEOFError 异常报告。

  • 方法 unwrap() 调用不返回任何内容,这与 SSL 套接字不同,后者返回底层套接字。

  • 传递给 SSLContext.set_servername_callback()server_name_callback 回调将获得一个 SSLObject 实例,而不是 SSLSocket 实例作为其第一个参数。

有关使用 SSLObject 的一些注意事项

在 3.7 版更改: 现在必须使用 wrap_bio() 创建 SSLObject 实例。在早期版本中,可以直接创建实例。这从未被记录或官方支持。

SSLObject 使用内存缓冲区与外部世界进行通信。类 MemoryBIO 提供了一个可用于此目的的内存缓冲区。它包装了一个 OpenSSL 内存 BIO(基本 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 将变为 true。

SSL 会话

版本 3.6 中的新功能。

class ssl.SSLSession

session 使用的会话对象。

id
time
timeout
ticket_lifetime_hint
has_ticket

安全注意事项

最佳默认设置

对于**客户端使用**,如果您对安全策略没有任何特殊要求,强烈建议您使用 create_default_context() 函数来创建 SSL 上下文。它将加载系统的受信任 CA 证书,启用证书验证和主机名检查,并尝试选择合理安全的协议和密码设置。

例如,以下是您如何使用 smtplib.SMTP 类创建与 SMTP 服务器的可信、安全连接

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

如果连接需要客户端证书,可以使用 SSLContext.load_cert_chain() 添加。

相比之下,如果您通过调用 SSLContext 构造函数自己创建 SSL 上下文,则默认情况下不会启用证书验证和主机名检查。如果您这样做,请阅读以下段落以达到良好的安全级别。

手动设置

验证证书

直接调用 SSLContext 构造函数时,默认值为 CERT_NONE。由于它不验证对方,因此可能不安全,尤其是在客户端模式下,您通常希望确保与之通信的服务器的真实性。因此,在客户端模式下,强烈建议使用 CERT_REQUIRED。但是,它本身还不够;您还必须检查服务器证书(可以通过调用 SSLSocket.getpeercert() 获取)是否与所需服务匹配。对于许多协议和应用程序,可以通过主机名来标识服务。启用 SSLContext.check_hostname 时,将自动执行此常见检查。

在 3.7 版更改: 主机名匹配现在由 OpenSSL 执行。Python 不再使用 match_hostname()

在服务器模式下,如果要使用 SSL 层(而不是使用更高级别的身份验证机制)对客户端进行身份验证,则还必须指定 CERT_REQUIRED 并类似地检查客户端证书。

协议版本

SSL 版本 2 和 3 被认为是不安全的,因此使用它们很危险。如果您希望客户端和服务器之间实现最大兼容性,建议使用 PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVER 作为协议版本。默认情况下,SSLv2 和 SSLv3 被禁用。

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

上面创建的 SSL 上下文只允许与服务器建立 TLSv1.3 及更高版本(如果您的系统支持)的连接。PROTOCOL_TLS_CLIENT 默认情况下意味着证书验证和主机名检查。您必须将证书加载到上下文中。

密码选择

如果您有高级安全要求,可以通过 SSLContext.set_ciphers() 方法对协商 SSL 会话时启用的密码进行微调。从 Python 3.2.3 开始,ssl 模块默认禁用某些弱密码,但您可能希望进一步限制密码选择。请务必阅读 OpenSSL 关于 密码列表格式 的文档。如果要检查给定密码列表启用了哪些密码,请使用 SSLContext.get_ciphers() 或系统上的 openssl ciphers 命令。

多进程

如果将此模块用作多进程应用程序的一部分(例如,使用 multiprocessingconcurrent.futures 模块),请注意 OpenSSL 的内部随机数生成器不能正确处理派生的进程。如果应用程序使用 os.fork() 的任何 SSL 功能,则必须更改父进程的 PRNG 状态。任何对 RAND_add()RAND_bytes() 的成功调用都足够。

TLS 1.3

3.7 版新增。

TLS 1.3 协议的行为与以前版本的 TLS/SSL 略有不同。某些新的 TLS 1.3 功能尚不可用。

  • TLS 1.3 使用一组不相交的密码套件。默认情况下,所有 AES-GCM 和 ChaCha20 密码套件都已启用。方法 SSLContext.set_ciphers() 尚无法启用或禁用任何 TLS 1.3 密码,但 SSLContext.get_ciphers() 会返回它们。

  • 会话票证不再作为初始握手的一部分发送,并且处理方式也不同。SSLSocket.sessionSSLSession 与 TLS 1.3 不兼容。

  • 客户端证书也不再在初始握手中进行验证。服务器可以随时请求证书。客户端在从服务器发送或接收应用程序数据时处理证书请求。

  • TLS 1.3 功能(如早期数据、延迟的 TLS 客户端证书请求、签名算法配置和密钥更新)尚不支持。