ssl — 用于套接字对象的 TLS/SSL 包装器

源代码: Lib/ssl.py


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

注解

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

警告

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

可用性:非 WASI。

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

本节记录了 ssl 模块中的对象和函数;有关 TLS、SSL 和证书的更多一般信息,请读者参考底部的“另请参阅”部分中的文档。

此模块提供一个类 ssl.SSLSocket,该类派生自 socket.socket 类型,并提供一个类似套接字的包装器,该包装器还使用 SSL 对通过套接字传输的数据进行加密和解密。它支持其他方法,例如 getpeercert(),用于检索连接另一端的证书,cipher(),用于检索用于安全连接的密码或 get_verified_chain()get_unverified_chain(),用于检索证书链。

对于更复杂的应用程序,ssl.SSLContext 类有助于管理设置和证书,然后可以通过 SSLContext.wrap_socket() 方法创建的 SSL 套接字继承这些设置和证书。

在 3.5.3 版本中更改: 更新以支持与 OpenSSL 1.1.0 链接

在 3.6 版本中更改: OpenSSL 0.9.8、1.0.0 和 1.0.1 已弃用且不再支持。将来,ssl 模块将至少需要 OpenSSL 1.0.2 或 1.1.0。

在 3.10 版本中更改: PEP 644 已实现。ssl 模块需要 OpenSSL 1.1.1 或更高版本。

使用已弃用的常量和函数会导致弃用警告。

函数、常量和异常

套接字创建

SSLSocket 的实例必须使用 SSLContext.wrap_socket() 方法创建。辅助函数 create_default_context() 返回具有安全默认设置的新上下文。

具有默认上下文和 IPv4/IPv6 双栈的客户端套接字示例

import socket
import ssl

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

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

具有自定义上下文和 IPv4 的客户端套接字示例

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

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

在 localhost IPv4 上侦听的服务器套接字示例

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

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

上下文创建

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

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

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

cafilecapathcadata 表示用于证书验证的可选 CA 证书,如 SSLContext.load_verify_locations() 中所述。如果这三者都是 None,则此函数可以选择信任系统的默认 CA 证书。

这些设置是:PROTOCOL_TLS_CLIENTPROTOCOL_TLS_SERVEROP_NO_SSLv2OP_NO_SSLv3,以及不带 RC4 和不带未经身份验证的密码套件的高加密密码套件。将 SERVER_AUTH 作为 *purpose* 传递会将 verify_mode 设置为 CERT_REQUIRED,并且要么加载 CA 证书(当给定 *cafile*、*capath* 或 *cadata* 中的至少一个时),要么使用 SSLContext.load_default_certs() 加载默认 CA 证书。

当支持 keylog_filename 并且设置了环境变量 SSLKEYLOGFILE 时,create_default_context() 将启用密钥日志记录。

此上下文的默认设置包括 VERIFY_X509_PARTIAL_CHAINVERIFY_X509_STRICT。这些设置使底层的 OpenSSL 实现的行为更像 RFC 5280 的符合规范的实现,但会与较旧的 X.509 证书产生少量不兼容性。

注解

协议、选项、密码和其他设置可能会随时更改为更严格的值,而无需事先弃用。这些值表示兼容性和安全性之间的合理平衡。

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

注解

如果您发现某些较旧的客户端或服务器尝试使用此函数创建的 SSLContext 进行连接时,收到错误消息“协议或密码套件不匹配”,则可能是它们仅支持 SSL3.0,而此函数使用 OP_NO_SSLv3 将其排除在外。SSL3.0 被广泛认为是完全损坏的。如果您仍然希望继续使用此函数但仍然允许 SSL 3.0 连接,则可以使用以下代码重新启用它们

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

注解

此上下文默认启用 VERIFY_X509_STRICT,这可能会拒绝底层的 OpenSSL 实现原本会接受的 pre-RFC 5280 或格式错误的证书。虽然不建议禁用此选项,但您可以使用以下代码禁用它

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

在 3.4 版本中添加。

在 3.4.4 版本中更改: 从默认密码字符串中删除了 RC4。

在 3.6 版本中更改: 将 ChaCha20/Poly1305 添加到默认密码字符串中。

从默认密码字符串中删除了 3DES。

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

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

在 3.13 版本中更改: 上下文现在在其默认验证标志中使用 VERIFY_X509_PARTIAL_CHAINVERIFY_X509_STRICT

异常

exception ssl.SSLError

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

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

library

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

在 3.3 版本中添加。

reason

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

在 3.3 版本中添加。

exception ssl.SSLZeroReturnError

当尝试读取或写入并且 SSL 连接已干净关闭时引发的 SSLError 的子类。请注意,这并不意味着底层的传输(读取 TCP)已关闭。

在 3.3 版本中添加。

exception ssl.SSLWantReadError

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

在 3.3 版本中添加。

exception ssl.SSLWantWriteError

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

在 3.3 版本中添加。

异常 ssl.SSLSyscallError

SSLError 的子类,当在 SSL 套接字上尝试执行操作时遇到系统错误时引发。不幸的是,没有简单的方法来检查原始的 errno 编号。

在 3.3 版本中添加。

异常 ssl.SSLEOFError

SSLError 的子类,当 SSL 连接突然终止时引发。一般来说,当遇到此错误时不应尝试重用底层传输。

在 3.3 版本中添加。

异常 ssl.SSLCertVerificationError

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

3.7 版本新增。

verify_code

表示验证错误的数字错误代码。

verify_message

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

异常 ssl.CertificateError

SSLCertVerificationError 的别名。

在 3.7 版本中更改: 该异常现在是 SSLCertVerificationError 的别名。

随机数生成

ssl.RAND_bytes(num)

返回 num 个密码学上安全的伪随机字节。 如果 PRNG 未使用足够的数据进行种子初始化,或者当前 RAND 方法不支持该操作,则引发 SSLErrorRAND_status() 可用于检查 PRNG 的状态,RAND_add() 可用于为 PRNG 提供种子。

对于几乎所有应用程序,os.urandom() 是更好的选择。

阅读维基百科文章,密码学安全的伪随机数生成器(CSPRNG),了解密码学安全生成器的要求。

在 3.3 版本中添加。

ssl.RAND_status()

如果 SSL 伪随机数生成器已使用“足够”的随机性进行种子初始化,则返回 True ,否则返回 False 。 您可以使用 ssl.RAND_egd()ssl.RAND_add() 来增加伪随机数生成器的随机性。

ssl.RAND_add(bytes, entropy)

将给定的 bytes 混合到 SSL 伪随机数生成器中。参数 entropy (浮点数)是字符串中包含的熵的下限(因此您始终可以使用 0.0 )。 有关熵来源的更多信息,请参见RFC 1750

在 3.5 版本中更改: 现在接受可写的类字节对象

证书处理

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 版本中更改: 将输入时间解释为输入字符串中“GMT”时区指定的 UTC 时间。之前使用本地时区。 返回一个整数(输入格式中没有秒的小数部分)

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

给定一个受 SSL 保护的服务器的地址 addr,以 (hostname, port-number) 对的形式,获取服务器的证书,并将其作为 PEM 编码的字符串返回。如果指定了 ssl_version ,则使用该版本的 SSL 协议尝试连接到服务器。如果指定了 ca_certs ,它应该是一个包含根证书列表的文件,其格式与 SSLContext.load_verify_locations()cafile 参数使用的格式相同。该调用将尝试针对该组根证书验证服务器证书,如果验证尝试失败,则该调用将失败。可以使用 timeout 参数指定超时。

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

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

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

ssl.DER_cert_to_PEM_cert(DER_cert_bytes)

给定一个以 DER 编码的字节 Blob 形式的证书,返回同一证书的 PEM 编码的字符串版本。

ssl.PEM_cert_to_DER_cert(PEM_cert_string)

给定一个 ASCII PEM 字符串形式的证书,返回该证书的 DER 编码的字节序列。

ssl.get_default_verify_paths()

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

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

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

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

  • openssl_cafile - cafile 的硬编码路径,

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

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

在 3.4 版本中添加。

ssl.enum_certificates(store_name)

从 Windows 系统证书存储中检索证书。store_name 可以是 CAROOTMY 之一。Windows 也可能提供额外的证书存储。

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

示例

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

可用性:Windows。

在 3.4 版本中添加。

ssl.enum_crls(store_name)

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

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

可用性:Windows。

在 3.4 版本中添加。

常量

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

在版本 3.6 中添加。

ssl.CERT_NONE

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

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

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

ssl.CERT_OPTIONAL

SSLContext.verify_mode 的可能值。在客户端模式下,CERT_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

enum.IntFlag OP_* 常量的集合。

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 库是否内置支持 应用层协议协商 TLS 扩展,如 RFC 7301 中所述。

在 3.5 版本中添加。

ssl.HAS_NEVER_CHECK_COMMON_NAME

OpenSSL 库是否内置支持不检查主题通用名称,并且 SSLContext.hostname_checks_common_name 是否可写。

3.7 版本新增。

ssl.HAS_ECDH

OpenSSL 库是否内置支持基于椭圆曲线的 Diffie-Hellman 密钥交换。除非分发者明确禁用此功能,否则应为 True。

在 3.3 版本中添加。

ssl.HAS_SNI

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

3.2 版本中新增。

ssl.HAS_NPN

OpenSSL 库是否内置支持 下一协议协商,如 应用层协议协商 中所述。如果为 True,您可以使用 SSLContext.set_npn_protocols() 方法来声明您想要支持的协议。

在 3.3 版本中添加。

ssl.HAS_SSLv2

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

3.7 版本新增。

ssl.HAS_SSLv3

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

3.7 版本新增。

ssl.HAS_TLSv1

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

3.7 版本新增。

ssl.HAS_TLSv1_1

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

3.7 版本新增。

ssl.HAS_TLSv1_2

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

3.7 版本新增。

ssl.HAS_TLSv1_3

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

3.7 版本新增。

ssl.HAS_PSK

OpenSSL 库是否内置支持 TLS-PSK。

在 3.13 版本中添加。

ssl.CHANNEL_BINDING_TYPES

支持的 TLS 通道绑定类型列表。此列表中的字符串可以用作 SSLSocket.get_channel_binding() 的参数。

在 3.3 版本中添加。

ssl.OPENSSL_VERSION

解释器加载的 OpenSSL 库的版本字符串

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

3.2 版本中新增。

ssl.OPENSSL_VERSION_INFO

表示 OpenSSL 库的版本信息的五个整数的元组

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

3.2 版本中新增。

ssl.OPENSSL_VERSION_NUMBER

OpenSSL 库的原始版本号,以单个整数表示

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

3.2 版本中新增。

ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE
ssl.ALERT_DESCRIPTION_INTERNAL_ERROR
ALERT_DESCRIPTION_*

来自 RFC 5246 和其他规范的警报描述。IANA TLS 警报注册表包含此列表和对其含义进行定义的 RFC 的引用。

用作 SSLContext.set_servername_callback() 中回调函数的返回值。

在 3.4 版本中添加。

class ssl.AlertDescription

enum.IntEnum ALERT_DESCRIPTION_* 常量的集合。

在版本 3.6 中添加。

Purpose.SERVER_AUTH

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

在 3.4 版本中添加。

Purpose.CLIENT_AUTH

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

在 3.4 版本中添加。

class ssl.SSLErrorNumber

enum.IntEnum SSL_ERROR_* 常量的集合。

在版本 3.6 中添加。

class ssl.TLSVersion

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

以 DER 编码的字节列表形式返回 SSL 通道另一端提供的已验证证书链。如果禁用证书验证,则此方法的作用与 get_unverified_chain() 相同。

在 3.13 版本中添加。

SSLSocket.get_unverified_chain()

以 DER 编码的字节列表形式返回 SSL 通道另一端提供的原始证书链。

在 3.13 版本中添加。

SSLSocket.cipher()

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

SSLSocket.shared_ciphers()

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

在 3.5 版本中添加。

SSLSocket.compression()

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

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

在 3.3 版本中添加。

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

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

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

在 3.3 版本中添加。

SSLSocket.selected_alpn_protocol()

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

在 3.5 版本中添加。

SSLSocket.selected_npn_protocol()

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

在 3.3 版本中添加。

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

SSLSocket.unwrap()

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

SSLSocket.verify_client_post_handshake()

请求 TLS 1.3 客户端进行握手后身份验证 (PHA)。 PHA 只能在初始 TLS 握手后,从服务器端套接字为 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.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 版本起已弃用: 不带协议参数的 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 客户端 Hello 握手消息后被调用。服务器名称指示机制在 RFC 6066 第 3 节 - 服务器名称指示中指定。

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

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

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

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

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

设置基于椭圆曲线的 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 指定我们正在连接的服务的 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_version, minimum_versionSSLContext.options 都会影响上下文支持的 SSL 和 TLS 版本。实现不会阻止无效的组合。例如,options 中包含 OP_NO_TLSv1_2,且 maximum_version 设置为 TLSVersion.TLSv1_2 的上下文将无法建立 TLS 1.2 连接。

3.7 版本新增。

SSLContext.minimum_version

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

3.7 版本新增。

SSLContext.num_tickets

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

3.8 版本中新增。

SSLContext.options

一个整数,表示在此上下文中启用的 SSL 选项集。默认值为 OP_ALL,但你可以通过对它们进行“或”运算来指定其他选项,例如 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>
SSLContext.set_psk_client_callback(callback)

在客户端连接上启用 TLS-PSK(预共享密钥)身份验证。

一般来说,应该优先选择基于证书的身份验证,而不是此方法。

参数 callback 是一个可调用对象,其签名如下:def callback(hint: str | None) -> tuple[str | None, bytes]hint 参数是服务器发送的可选身份提示。返回值是一个元组,格式为 (client-identity, psk)。Client-identity 是一个可选字符串,服务器可以使用它来选择客户端的相应 PSK。当使用 UTF-8 编码时,该字符串必须小于或等于 256 个八位字节。PSK 是一个表示预共享密钥的 类字节对象。返回零长度 PSK 以拒绝连接。

callback 设置为 None 将删除任何现有的回调。

注解

当使用 TLS 1.3 时

  • hint 参数始终为 None

  • client-identity 必须是非空字符串。

用法示例

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

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

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

如果 HAS_PSKFalse,此方法将引发 NotImplementedError

在 3.13 版本中添加。

SSLContext.set_psk_server_callback(callback, identity_hint=None)

在服务器端连接上启用 TLS-PSK(预共享密钥)身份验证。

一般来说,应该优先选择基于证书的身份验证,而不是此方法。

参数 callback 是一个可调用对象,其签名如下:def callback(identity: str | None) -> bytesidentity 参数是客户端发送的可选身份标识,可用于选择相应的 PSK。返回值是一个类字节对象,表示预共享密钥。返回零长度的 PSK 将拒绝连接。

callback 设置为 None 将删除任何现有的回调。

参数 identity_hint 是发送给客户端的可选身份提示字符串。当使用 UTF-8 编码时,该字符串必须小于或等于 256 个八位字节。

注解

当使用 TLS 1.3 时,identity_hint 参数不会发送给客户端。

用法示例

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

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

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

如果 HAS_PSKFalse,此方法将引发 NotImplementedError

在 3.13 版本中添加。

证书

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

证书包含关于两个主体的信息。它包含一个主题的名称和主题的公钥。它还包含第二个主体(颁发者)的声明,表明该主题是其声称的身份,并且这确实是该主题的公钥。颁发者的声明使用颁发者的私钥签名,只有颁发者知道该私钥。但是,任何人都可以通过查找颁发者的公钥、使用该公钥解密该声明并将其与证书中的其他信息进行比较来验证颁发者的声明。证书还包含关于其有效时间段的信息。这表示为两个字段,称为“notBefore”和“notAfter”。

在 Python 中使用证书时,客户端或服务器可以使用证书来证明自己的身份。还可以要求网络连接的另一端提供证书,并且该证书可以被验证,以使需要此类验证的客户端或服务器满意。如果验证失败,连接尝试可以设置为引发异常。验证由底层的 OpenSSL 框架自动完成;应用程序无需关心其机制。但是,应用程序通常需要提供证书集,以允许此过程发生。

Python 使用文件来包含证书。它们应格式化为“PEM”(请参阅RFC 1422),这是一种 base-64 编码形式,并用标题行和页脚行包裹。

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

证书链

包含证书的 Python 文件可以包含一系列证书,有时称为证书链。此链应从“是”客户端或服务器的主体的特定证书开始,然后是该证书的颁发者的证书,然后是证书的颁发者的证书,依此类推,直到获得一个自签名的证书,即具有相同主题和颁发者的证书,有时称为根证书。这些证书应该只是在证书文件中连接在一起。例如,假设我们有一个三证书链,从我们的服务器证书到签署我们服务器证书的证书颁发机构的证书,再到颁发证书颁发机构证书的机构的根证书。

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

CA 证书

如果要要求验证连接另一端的证书,则需要提供一个“CA 证书”文件,其中填充了您愿意信任的每个颁发者的证书链。同样,此文件只是包含这些连接在一起的链。对于验证,Python 将使用文件中找到的第一个匹配链。可以通过调用SSLContext.load_default_certs()来使用平台的证书文件,这可以通过create_default_context()自动完成。

组合密钥和证书

通常,私钥与证书存储在同一个文件中;在这种情况下,只需传递SSLContext.load_cert_chain()certfile 参数。如果私钥与证书一起存储,则它应该位于证书链中的第一个证书之前。

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

自签名证书

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

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

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

示例

测试 SSL 支持

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

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

客户端操作

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

>>> context = ssl.create_default_context()

如果您希望自己调整安全设置,则可以从头开始创建一个上下文(但请注意,您可能无法获得正确的设置):

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

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

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

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

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

然后,您可以获取证书:

>>> cert = conn.getpeercert()

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

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

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

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

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

服务器端操作

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

import socket, ssl

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

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

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

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

然后,您将从 connstream 中读取数据并对其执行某些操作,直到您完成客户端的操作(或客户端完成您的操作):

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

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

关于非阻塞套接字的说明

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

  • 如果 I/O 操作将阻塞,则大多数 SSLSocket 方法将引发 SSLWantWriteErrorSSLWantReadError,而不是 BlockingIOError。如果底层套接字上需要读取操作,则会引发 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 套接字 并提供更高级别的 Streams API。 它使用 selectors 模块轮询事件,并处理 SSLWantWriteErrorSSLWantReadErrorBlockingIOError 异常。 它也异步运行 SSL 握手。

内存 BIO 支持

在 3.5 版本中添加。

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

  • SSL 协议处理

  • 网络 IO

网络 IO API 与 socket.socket 提供的 API 相同,SSLSocket 也从中继承。 这允许将 SSL 套接字用作常规套接字的直接替换,从而使向现有应用程序添加 SSL 支持非常容易。

组合 SSL 协议处理和网络 IO 通常可以很好地工作,但在某些情况下却不行。 一个示例是异步 IO 框架,它们希望使用与 socket.socket 和内部 OpenSSL 套接字 IO 例程所假设的“基于文件描述符的 select/poll” (基于就绪状态)模型不同的 IO 多路复用模型。 这主要与 Windows 等平台相关,在这种平台上此模型效率不高。 为此,提供了一个缩小范围的 SSLSocket 变体,称为 SSLObject

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 异常报告。

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

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

一些与使用 SSLObject 相关的重要说明

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

SSLObject 使用内存缓冲区与外部世界通信。 类 MemoryBIO 提供了一个可用于此目的的内存缓冲区。 它包装了一个 OpenSSL 内存 BIO(基本 IO)对象

class ssl.MemoryBIO

一个内存缓冲区,可用于在 Python 和 SSL 协议实例之间传递数据。

pending

返回当前内存缓冲区中的字节数。

eof

一个布尔值,指示内存 BIO 是否当前处于文件结束位置。

read(n=-1)

从内存缓冲区读取最多 n 个字节。如果未指定 nn 为负数,则返回所有字节。

write(buf)

buf 中的字节写入内存 BIO。buf 参数必须是支持缓冲区协议的对象。

返回值是写入的字节数,始终等于 buf 的长度。

write_eof()

向内存 BIO 写入 EOF 标记。调用此方法后,再调用 write() 是非法的。在读取缓冲区中所有数据后,属性 eof 将变为真。

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 客户端证书请求、签名算法配置和重新密钥。