ssl
— 用于套接字对象的 TLS/SSL 包装器¶
源代码: Lib/ssl.py
此模块提供对网络套接字的传输层安全性(通常称为“安全套接字层”)加密和对等身份验证功能的访问,包括客户端和服务器端。此模块使用 OpenSSL 库。只要该平台上安装了 OpenSSL,它就可以在所有现代 Unix 系统、Windows、macOS 以及可能的其他平台上使用。
注解
某些行为可能依赖于平台,因为会调用操作系统套接字 API。安装的 OpenSSL 版本也可能导致行为上的差异。例如,TLSv1.3 随 OpenSSL 1.1.1 版本一起提供。
警告
在阅读安全注意事项之前,请勿使用此模块。这样做可能会导致虚假的安全感,因为 ssl 模块的默认设置不一定适合您的应用程序。
可用性:非 WASI。
此模块在 WebAssembly 上不起作用或不可用。有关更多信息,请参阅WebAssembly 平台。
本节记录了 ssl
模块中的对象和函数;有关 TLS、SSL 和证书的更多一般信息,请读者参考底部的“另请参阅”部分中的文档。
此模块提供一个类 ssl.SSLSocket
,该类派生自 socket.socket
类型,并提供一个类似套接字的包装器,该包装器还使用 SSL 对通过套接字传输的数据进行加密和解密。它支持其他方法,例如 getpeercert()
,用于检索连接另一端的证书,cipher()
,用于检索用于安全连接的密码或 get_verified_chain()
, get_unverified_chain()
,用于检索证书链。
对于更复杂的应用程序,ssl.SSLContext
类有助于管理设置和证书,然后可以通过 SSLContext.wrap_socket()
方法创建的 SSL 套接字继承这些设置和证书。
在 3.5.3 版本中更改: 更新以支持与 OpenSSL 1.1.0 链接
在 3.6 版本中更改: OpenSSL 0.9.8、1.0.0 和 1.0.1 已弃用且不再支持。将来,ssl 模块将至少需要 OpenSSL 1.0.2 或 1.1.0。
在 3.10 版本中更改: PEP 644 已实现。ssl 模块需要 OpenSSL 1.1.1 或更高版本。
使用已弃用的常量和函数会导致弃用警告。
函数、常量和异常¶
套接字创建¶
SSLSocket
的实例必须使用 SSLContext.wrap_socket()
方法创建。辅助函数 create_default_context()
返回具有安全默认设置的新上下文。
具有默认上下文和 IPv4/IPv6 双栈的客户端套接字示例
import socket
import ssl
hostname = 'www.python.org'
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
具有自定义上下文和 IPv4 的客户端套接字示例
hostname = 'www.python.org'
# PROTOCOL_TLS_CLIENT requires valid cert chain and hostname
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('path/to/cabundle.pem')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
在 localhost IPv4 上侦听的服务器套接字示例
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('/path/to/certchain.pem', '/path/to/private.key')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
sock.bind(('127.0.0.1', 8443))
sock.listen(5)
with context.wrap_socket(sock, server_side=True) as ssock:
conn, addr = ssock.accept()
...
上下文创建¶
一个便捷函数有助于为常见目的创建 SSLContext
对象。
- ssl.create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)¶
返回一个新的
SSLContext
对象,该对象具有给定 *purpose* 的默认设置。这些设置由ssl
模块选择,通常比直接调用SSLContext
构造函数具有更高的安全级别。cafile、capath、cadata 表示用于证书验证的可选 CA 证书,如
SSLContext.load_verify_locations()
中所述。如果这三者都是None
,则此函数可以选择信任系统的默认 CA 证书。这些设置是:
PROTOCOL_TLS_CLIENT
或PROTOCOL_TLS_SERVER
、OP_NO_SSLv2
和OP_NO_SSLv3
,以及不带 RC4 和不带未经身份验证的密码套件的高加密密码套件。将SERVER_AUTH
作为 *purpose* 传递会将verify_mode
设置为CERT_REQUIRED
,并且要么加载 CA 证书(当给定 *cafile*、*capath* 或 *cadata* 中的至少一个时),要么使用SSLContext.load_default_certs()
加载默认 CA 证书。当支持
keylog_filename
并且设置了环境变量SSLKEYLOGFILE
时,create_default_context()
将启用密钥日志记录。此上下文的默认设置包括
VERIFY_X509_PARTIAL_CHAIN
和VERIFY_X509_STRICT
。这些设置使底层的 OpenSSL 实现的行为更像 RFC 5280 的符合规范的实现,但会与较旧的 X.509 证书产生少量不兼容性。注解
协议、选项、密码和其他设置可能会随时更改为更严格的值,而无需事先弃用。这些值表示兼容性和安全性之间的合理平衡。
如果您的应用程序需要特定的设置,则应创建一个
SSLContext
并自行应用这些设置。注解
如果您发现某些较旧的客户端或服务器尝试使用此函数创建的
SSLContext
进行连接时,收到错误消息“协议或密码套件不匹配”,则可能是它们仅支持 SSL3.0,而此函数使用OP_NO_SSLv3
将其排除在外。SSL3.0 被广泛认为是完全损坏的。如果您仍然希望继续使用此函数但仍然允许 SSL 3.0 连接,则可以使用以下代码重新启用它们ctx = ssl.create_default_context(Purpose.CLIENT_AUTH) ctx.options &= ~ssl.OP_NO_SSLv3
注解
此上下文默认启用
VERIFY_X509_STRICT
,这可能会拒绝底层的 OpenSSL 实现原本会接受的 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_CLIENT
或PROTOCOL_TLS_SERVER
协议,而不是通用的PROTOCOL_TLS
。在 3.13 版本中更改: 上下文现在在其默认验证标志中使用
VERIFY_X509_PARTIAL_CHAIN
和VERIFY_X509_STRICT
。
异常¶
- exception ssl.SSLError¶
当底层 SSL 实现(当前由 OpenSSL 库提供)发出错误信号时引发。这表示在叠加在底层网络连接之上的更高层的加密和身份验证层中存在一些问题。此错误是
OSError
的子类型。SSLError
实例的错误代码和消息由 OpenSSL 库提供。在 3.3 版本中更改:
SSLError
曾经是socket.error
的子类型。- library¶
一个字符串助记符,用于指定发生错误的 OpenSSL 子模块,例如
SSL
、PEM
或X509
。可能值的范围取决于 OpenSSL 版本。在 3.3 版本中添加。
- reason¶
一个字符串助记符,用于指定发生此错误的原因,例如
CERTIFICATE_VERIFY_FAILED
。可能值的范围取决于 OpenSSL 版本。在 3.3 版本中添加。
- exception ssl.SSLZeroReturnError¶
当尝试读取或写入并且 SSL 连接已干净关闭时引发的
SSLError
的子类。请注意,这并不意味着底层的传输(读取 TCP)已关闭。在 3.3 版本中添加。
- exception ssl.SSLWantReadError¶
当尝试读取或写入数据时,非阻塞 SSL 套接字 引发的
SSLError
的子类,但在请求可以完成之前,需要在底层的 TCP 传输上接收更多数据。在 3.3 版本中添加。
- exception ssl.SSLWantWriteError¶
当尝试读取或写入数据时,非阻塞 SSL 套接字 引发的
SSLError
的子类,但在请求可以完成之前,需要在底层的 TCP 传输上发送更多数据。在 3.3 版本中添加。
- 异常 ssl.SSLSyscallError¶
是
SSLError
的子类,当在 SSL 套接字上尝试执行操作时遇到系统错误时引发。不幸的是,没有简单的方法来检查原始的 errno 编号。在 3.3 版本中添加。
- 异常 ssl.SSLCertVerificationError¶
是
SSLError
的子类,当证书验证失败时引发。3.7 版本新增。
- verify_code¶
表示验证错误的数字错误代码。
- verify_message¶
表示验证错误的人类可读的字符串。
- 异常 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.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_version 从
PROTOCOL_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 可以是
CA
、ROOT
或MY
之一。Windows 也可能提供额外的证书存储。该函数返回一个 (cert_bytes, encoding_type, trust) 元组的列表。encoding_type 指定 cert_bytes 的编码。它可以是 X.509 ASN.1 数据的
x509_asn
,或者 PKCS#7 ASN.1 数据的pkcs_7_asn
。Trust 将证书的目的指定为一组 OID,或者如果证书对所有目的都可信,则精确地指定为True
。示例
>>> ssl.enum_certificates("CA") [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}), (b'data...', 'x509_asn', True)]
可用性:Windows。
在 3.4 版本中添加。
常量¶
所有常量现在都是
enum.IntEnum
或enum.IntFlag
集合。在版本 3.6 中添加。
- ssl.CERT_NONE¶
SSLContext.verify_mode
的可能值。除了PROTOCOL_TLS_CLIENT
之外,它是默认模式。使用客户端套接字时,几乎接受任何证书。验证错误(例如不受信任或过期的证书)将被忽略,并且不会中止 TLS/SSL 握手。在服务器模式下,不会向客户端请求证书,因此客户端不会发送任何证书以进行客户端证书身份验证。
请参阅下面的安全注意事项的讨论。
- ssl.CERT_OPTIONAL¶
SSLContext.verify_mode
的可能值。在客户端模式下,CERT_OPTIONAL
与CERT_REQUIRED
具有相同的含义。建议对客户端套接字使用CERT_REQUIRED
。在服务器模式下,会向客户端发送客户端证书请求。客户端可以忽略该请求或发送证书以执行 TLS 客户端证书身份验证。如果客户端选择发送证书,则会对其进行验证。任何验证错误都会立即中止 TLS 握手。
使用此设置需要将有效的 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_CLIENT
和PROTOCOL_TLS_SERVER
。
- ssl.PROTOCOL_TLS_CLIENT¶
自动协商客户端和服务器都支持的最高协议版本,并配置上下文客户端连接。该协议默认启用
CERT_REQUIRED
和check_hostname
。在版本 3.6 中添加。
- ssl.PROTOCOL_TLS_SERVER¶
自动协商客户端和服务器都支持的最高协议版本,并配置上下文服务器端连接。
在版本 3.6 中添加。
- ssl.PROTOCOL_SSLv23¶
是
PROTOCOL_TLS
的别名。自 3.6 版本起已弃用: 请改用
PROTOCOL_TLS
。
- ssl.PROTOCOL_SSLv3¶
选择 SSL 版本 3 作为通道加密协议。
如果 OpenSSL 是使用
no-ssl3
选项编译的,则此协议不可用。警告
SSL 版本 3 不安全。强烈建议不要使用它。
自 3.6 版本起已弃用: OpenSSL 已弃用所有特定版本的协议。请改用默认协议
PROTOCOL_TLS_SERVER
或PROTOCOL_TLS_CLIENT
,并使用SSLContext.minimum_version
和SSLContext.maximum_version
。
- ssl.PROTOCOL_TLSv1¶
选择 TLS 版本 1.0 作为通道加密协议。
自 3.6 版本起已弃用: OpenSSL 已弃用所有特定版本的协议。
- ssl.PROTOCOL_TLSv1_1¶
选择 TLS 版本 1.1 作为通道加密协议。仅在 openssl 版本 1.0.1+ 中可用。
在 3.4 版本中添加。
自 3.6 版本起已弃用: OpenSSL 已弃用所有特定版本的协议。
- ssl.PROTOCOL_TLSv1_2¶
选择 TLS 版本 1.2 作为通道加密协议。仅在 openssl 版本 1.0.1+ 中可用。
在 3.4 版本中添加。
自 3.6 版本起已弃用: OpenSSL 已弃用所有特定版本的协议。
- ssl.OP_ALL¶
启用对其他 SSL 实现中存在的各种错误的解决方法。默认情况下会设置此选项。它不一定会设置与 OpenSSL 的
SSL_OP_ALL
常量相同的标志。3.2 版本中新增。
- ssl.OP_NO_SSLv2¶
阻止 SSLv2 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 SSLv2 作为协议版本。3.2 版本中新增。
自 3.6 版本起已弃用: SSLv2 已被弃用
- ssl.OP_NO_SSLv3¶
阻止 SSLv3 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 SSLv3 作为协议版本。3.2 版本中新增。
自 3.6 版本起已弃用: SSLv3 已被弃用
- ssl.OP_NO_TLSv1¶
阻止 TLSv1 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 TLSv1 作为协议版本。3.2 版本中新增。
自 3.7 版本起已弃用: 自 OpenSSL 1.1.0 起,该选项已被弃用,请改用新的
SSLContext.minimum_version
和SSLContext.maximum_version
。
- ssl.OP_NO_TLSv1_1¶
阻止 TLSv1.1 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 TLSv1.1 作为协议版本。仅在 openssl 版本 1.0.1+ 中可用。在 3.4 版本中添加。
自 3.7 版本起已弃用: 自 OpenSSL 1.1.0 起,该选项已被弃用。
- ssl.OP_NO_TLSv1_2¶
阻止 TLSv1.2 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 TLSv1.2 作为协议版本。仅在 openssl 版本 1.0.1+ 中可用。在 3.4 版本中添加。
自 3.7 版本起已弃用: 自 OpenSSL 1.1.0 起,该选项已被弃用。
- ssl.OP_NO_TLSv1_3¶
阻止 TLSv1.3 连接。此选项仅适用于
PROTOCOL_TLS
。它阻止对等方选择 TLSv1.3 作为协议版本。TLS 1.3 在 OpenSSL 1.1.1 或更高版本中可用。当 Python 是针对旧版本的 OpenSSL 编译的时,该标志默认为 0。3.6.3 版本中新增。
自 3.7 版本起已弃用: 自 OpenSSL 1.1.0 起,该选项已被弃用。为了向后兼容 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_NEVER_CHECK_COMMON_NAME¶
OpenSSL 库是否内置支持不检查主题通用名称,并且
SSLContext.hostname_checks_common_name
是否可写。3.7 版本新增。
- ssl.HAS_ECDH¶
OpenSSL 库是否内置支持基于椭圆曲线的 Diffie-Hellman 密钥交换。除非分发者明确禁用此功能,否则应为 True。
在 3.3 版本中添加。
- ssl.HAS_NPN¶
OpenSSL 库是否内置支持 下一协议协商,如 应用层协议协商 中所述。如果为 True,您可以使用
SSLContext.set_npn_protocols()
方法来声明您想要支持的协议。在 3.3 版本中添加。
- ssl.HAS_SSLv2¶
OpenSSL 库是否内置支持 SSL 2.0 协议。
3.7 版本新增。
- ssl.HAS_SSLv3¶
OpenSSL 库是否内置支持 SSL 3.0 协议。
3.7 版本新增。
- ssl.HAS_TLSv1¶
OpenSSL 库是否内置支持 TLS 1.0 协议。
3.7 版本新增。
- ssl.HAS_TLSv1_1¶
OpenSSL 库是否内置支持 TLS 1.1 协议。
3.7 版本新增。
- ssl.HAS_TLSv1_2¶
OpenSSL 库是否内置支持 TLS 1.2 协议。
3.7 版本新增。
- ssl.HAS_TLSv1_3¶
OpenSSL 库是否内置支持 TLS 1.3 协议。
3.7 版本新增。
- ssl.HAS_PSK¶
OpenSSL 库是否内置支持 TLS-PSK。
在 3.13 版本中添加。
- ssl.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_version
和SSLContext.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_2
和TLSVersion.TLSv1_3
之外的所有TLSVersion
成员都已弃用。
SSL 套接字¶
- class ssl.SSLSocket(socket.socket)¶
SSL 套接字提供 套接字对象 的以下方法
recv()
,recv_into()
(但传递非零的flags
参数是不允许的)sendfile()
(但是os.sendfile
仅用于纯文本套接字,否则将使用send()
)
然而,由于 SSL(和 TLS)协议在 TCP 之上拥有自己的帧结构,SSL 套接字抽象在某些方面可能与普通操作系统级别套接字的规范有所不同。尤其请参阅 关于非阻塞套接字的说明。
SSLSocket
的实例必须使用SSLContext.wrap_socket()
方法创建。3.5 版本更改: 添加了
sendfile()
方法。3.5 版本更改:
shutdown()
不再在每次接收或发送字节时重置套接字超时。套接字超时现在是关闭的最大总持续时间。3.6 版本起已弃用: 不推荐直接创建
SSLSocket
实例,请使用SSLContext.wrap_socket()
来包装套接字。3.7 版本更改:
SSLSocket
实例必须使用wrap_socket()
创建。在早期版本中,可以直接创建实例。但这从未被记录或官方支持。3.10 版本更改: Python 现在在内部使用
SSL_read_ex
和SSL_write_ex
。这些函数支持读取和写入大于 2 GB 的数据。写入零长度的数据不再会因协议违规错误而失败。
SSL 套接字还具有以下附加方法和属性
- SSLSocket.read(len=1024, buffer=None)¶
从 SSL 套接字读取最多 len 字节的数据,并将结果作为
bytes
实例返回。如果指定了 buffer,则改为读取到缓冲区中,并返回读取的字节数。如果套接字是 非阻塞的 并且读取会阻塞,则引发
SSLWantReadError
或SSLWantWriteError
。由于随时可能进行重新协商,因此调用
read()
也可能导致写入操作。3.5 版本更改: 套接字超时不再在每次接收或发送字节时重置。套接字超时现在是读取最多 len 字节的最大总持续时间。
3.6 版本起已弃用: 使用
recv()
而不是read()
。
- SSLSocket.write(buf)¶
将 buf 写入 SSL 套接字,并返回写入的字节数。 buf 参数必须是支持缓冲区接口的对象。
如果套接字是 非阻塞的 并且写入会阻塞,则引发
SSLWantReadError
或SSLWantWriteError
。由于随时可能进行重新协商,因此调用
write()
也可能导致读取操作。3.5 版本更改: 套接字超时不再在每次接收或发送字节时重置。套接字超时现在是写入 buf 的最大总持续时间。
3.6 版本起已弃用: 使用
send()
而不是write()
。
注解
read()
和 write()
方法是底层方法,用于读取和写入未加密的应用程序级别数据,并将其解密/加密为加密的线路级别数据。这些方法需要活动的 SSL 连接,即已完成握手且未调用 SSLSocket.unwrap()
。
- SSLSocket.do_handshake()¶
执行 SSL 设置握手。
3.4 版本更改: 当套接字的
context
的check_hostname
属性为 true 时,握手方法还会执行match_hostname()
。3.5 版本更改: 套接字超时不再在每次接收或发送字节时重置。套接字超时现在是握手的最大总持续时间。
3.7 版本更改: 主机名或 IP 地址在握手期间由 OpenSSL 匹配。不再使用函数
match_hostname()
。如果 OpenSSL 拒绝主机名或 IP 地址,握手会提前中止,并向对等方发送 TLS 警报消息。
- SSLSocket.getpeercert(binary_form=False)¶
如果连接另一端的对等方没有证书,则返回
None
。如果尚未完成 SSL 握手,则引发ValueError
。如果
binary_form
参数为False
,并且从对等方收到证书,则此方法返回一个dict
实例。 如果证书未经过验证,则字典为空。如果证书经过验证,则它返回一个包含多个键的字典,其中包括subject
(颁发证书的主体)和issuer
(颁发证书的主体)。如果证书包含 *Subject Alternative Name* 扩展的实例(请参阅RFC 3280),则字典中还会有一个subjectAltName
键。subject
和issuer
字段是元组,其中包含证书数据结构中各自字段给出的相对可分辨名称 (RDN) 序列,每个 RDN 都是名称-值对的序列。这是一个真实的示例{'issuer': ((('countryName', 'IL'),), (('organizationName', 'StartCom Ltd.'),), (('organizationalUnitName', 'Secure Digital Certificate Signing'),), (('commonName', 'StartCom Class 2 Primary Intermediate Server CA'),)), 'notAfter': 'Nov 22 08:15:19 2013 GMT', 'notBefore': 'Nov 21 03:09:52 2011 GMT', 'serialNumber': '95F0', 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),), (('countryName', 'US'),), (('stateOrProvinceName', 'California'),), (('localityName', 'San Francisco'),), (('organizationName', 'Electronic Frontier Foundation, Inc.'),), (('commonName', '*.eff.org'),), (('emailAddress', '[email protected]'),)), 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')), 'version': 3}
如果
binary_form
参数为True
,并且提供了证书,则此方法将以字节序列形式返回整个证书的 DER 编码形式,或者如果对等方未提供证书,则返回None
。对等方是否提供证书取决于 SSL 套接字的角色对于客户端 SSL 套接字,无论是否需要验证,服务器都将始终提供证书;
对于服务器 SSL 套接字,仅当服务器请求时,客户端才会提供证书;因此,如果您使用
CERT_NONE
(而不是CERT_OPTIONAL
或CERT_REQUIRED
),getpeercert()
将返回None
。
另请参阅
SSLContext.check_hostname
。3.2 版本更改: 返回的字典包含其他项,如
issuer
和notBefore
。3.4 版本更改: 当握手未完成时,将引发
ValueError
。返回的字典包含其他 X509v3 扩展项,如crlDistributionPoints
、caIssuers
和OCSP
URI。3.9 版本更改: IPv6 地址字符串不再包含尾随换行符。
- SSLSocket.get_verified_chain()¶
以 DER 编码的字节列表形式返回 SSL 通道另一端提供的已验证证书链。如果禁用证书验证,则此方法的作用与
get_unverified_chain()
相同。在 3.13 版本中添加。
- SSLSocket.get_unverified_chain()¶
以 DER 编码的字节列表形式返回 SSL 通道另一端提供的原始证书链。
在 3.13 版本中添加。
- SSLSocket.cipher()¶
返回一个三值元组,其中包含正在使用的密码的名称、定义其使用的 SSL 协议的版本以及正在使用的秘密位数。 如果尚未建立连接,则返回
None
。
返回客户端和服务器中都可用的密码列表。 返回的列表的每个条目都是一个三值元组,其中包含密码的名称、定义其使用的 SSL 协议的版本以及密码使用的秘密位数。 如果未建立连接或套接字是客户端套接字,则
shared_ciphers()
返回None
。在 3.5 版本中添加。
- SSLSocket.compression()¶
以字符串形式返回正在使用的压缩算法,如果连接未压缩,则返回
None
。如果更高级别的协议支持其自身的压缩机制,则可以使用
OP_NO_COMPRESSION
来禁用 SSL 级别的压缩。在 3.3 版本中添加。
- SSLSocket.get_channel_binding(cb_type='tls-unique')¶
获取当前连接的通道绑定数据,作为字节对象。如果未连接或握手尚未完成,则返回
None
。cb_type 参数允许选择所需的通道绑定类型。 有效的通道绑定类型在
CHANNEL_BINDING_TYPES
列表中列出。目前仅支持由 RFC 5929 定义的 'tls-unique' 通道绑定。 如果请求不支持的通道绑定类型,则会引发ValueError
。在 3.3 版本中添加。
- SSLSocket.selected_alpn_protocol()¶
返回在 TLS 握手期间选择的协议。 如果未调用
SSLContext.set_alpn_protocols()
,如果另一方不支持 ALPN,如果此套接字不支持任何客户端的提议协议,或者如果尚未发生握手,则返回None
。在 3.5 版本中添加。
- SSLSocket.selected_npn_protocol()¶
返回在 TLS/SSL 握手期间选择的更高级别协议。 如果未调用
SSLContext.set_npn_protocols()
,或者如果另一方不支持 NPN,或者如果握手尚未发生,则这将返回None
。在 3.3 版本中添加。
自 3.10 版本弃用: NPN 已被 ALPN 取代
- SSLSocket.unwrap()¶
执行 SSL 关闭握手,这将从底层套接字中删除 TLS 层,并返回底层套接字对象。这可用于从连接上的加密操作转换为未加密操作。 返回的套接字应始终用于与连接的另一端进行进一步通信,而不是原始套接字。
- SSLSocket.verify_client_post_handshake()¶
请求 TLS 1.3 客户端进行握手后身份验证 (PHA)。 PHA 只能在初始 TLS 握手后,从服务器端套接字为 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_COMPRESSION
、OP_CIPHER_SERVER_PREFERENCE
、OP_SINGLE_DH_USE
、OP_SINGLE_ECDH_USE
、OP_NO_SSLv2
和OP_NO_SSLv3
(PROTOCOL_SSLv3
除外)默认设置。初始密码套件列表仅包含HIGH
密码,不包含NULL
密码,也不包含MD5
密码。3.10 版本起已弃用: 不带协议参数的
SSLContext
已弃用。上下文类将来将需要PROTOCOL_TLS_CLIENT
或PROTOCOL_TLS_SERVER
协议。3.10 版本更改: 默认密码套件现在仅包含具有前向保密和安全级别 2 的安全 AES 和 ChaCha20 密码。禁止使用少于 2048 位的 RSA 和 DH 密钥以及少于 224 位的 ECC 密钥。
PROTOCOL_TLS
、PROTOCOL_TLS_CLIENT
和PROTOCOL_TLS_SERVER
使用 TLS 1.2 作为最低 TLS 版本。注解
SSLContext
一旦被连接使用,仅支持有限的变动。允许向内部信任存储添加新证书,但更改密码、验证设置或 mTLS 证书可能会导致意外行为。注解
SSLContext
旨在被多个连接共享和使用。因此,只要在被连接使用后不重新配置,它就是线程安全的。
SSLContext
对象具有以下方法和属性
- SSLContext.cert_store_stats()¶
获取关于已加载的 X.509 证书数量、标记为 CA 证书的 X.509 证书计数以及证书吊销列表的统计信息,以字典形式返回。
一个包含一个 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 上,它从
CA
和ROOT
系统存储中加载 CA 证书。在所有系统上,它都会调用SSLContext.set_default_verify_paths()
。将来,该方法也可能会从其他位置加载 CA 证书。purpose 标志指定加载哪种 CA 证书。默认设置
Purpose.SERVER_AUTH
加载标记并信任用于 TLS Web 服务器身份验证(客户端套接字)的证书。Purpose.CLIENT_AUTH
加载用于服务器端客户端证书验证的 CA 证书。在 3.4 版本中添加。
- SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)¶
加载一组“证书颁发机构”(CA)证书,用于在
verify_mode
不是CERT_NONE
时验证其他对等方的证书。必须至少指定 cafile 或 capath 之一。此方法还可以加载 PEM 或 DER 格式的证书吊销列表(CRL)。为了使用 CRL,必须正确配置
SSLContext.verify_flags
。cafile 字符串(如果存在)是 PEM 格式的串联 CA 证书文件的路径。有关如何在此文件中排列证书的更多信息,请参阅 证书 的讨论。
capath 字符串(如果存在)是包含多个 PEM 格式的 CA 证书的目录的路径,遵循 OpenSSL 特定布局。
cadata 对象(如果存在)是一个或多个 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
。
- SSLContext.set_alpn_protocols(protocols)¶
指定套接字在 SSL/TLS 握手期间应通告的协议。它应该是一个 ASCII 字符串列表,例如
['http/1.1', 'spdy/2']
,按优先级排序。协议的选择将在握手期间发生,并将按照 RFC 7301 进行。成功握手后,SSLSocket.selected_alpn_protocol()
方法将返回商定的协议。如果
HAS_ALPN
为False
,则此方法将引发NotImplementedError
。在 3.5 版本中添加。
- SSLContext.set_npn_protocols(protocols)¶
指定套接字在 SSL/TLS 握手期间应通告的协议。它应该是一个字符串列表,例如
['http/1.1', 'spdy/2']
,按优先级排序。协议的选择将在握手期间发生,并将根据应用层协议协商进行。成功握手后,SSLSocket.selected_npn_protocol()
方法将返回商定的协议。如果
HAS_NPN
为False
,则此方法将引发NotImplementedError
。在 3.3 版本中添加。
自 3.10 版本弃用: NPN 已被 ALPN 取代
- SSLContext.sni_callback¶
注册一个回调函数,该函数将在 TLS 客户端指定服务器名称指示时,在 SSL/TLS 服务器接收到 TLS 客户端 Hello 握手消息后被调用。服务器名称指示机制在 RFC 6066 第 3 节 - 服务器名称指示中指定。
每个
SSLContext
只能设置一个回调。如果 sni_callback 设置为None
,则禁用回调。随后调用此函数将禁用之前注册的回调。回调函数将使用三个参数调用;第一个是
ssl.SSLSocket
,第二个是一个字符串,表示客户端打算通信的服务器名称(如果 TLS 客户端 Hello 不包含服务器名称,则为None
),第三个参数是原始的SSLContext
。服务器名称参数是文本。对于国际化域名,服务器名称是 IDN A 标签("xn--pythn-mua.org"
)。此回调的典型用法是将
ssl.SSLSocket
的SSLSocket.context
属性更改为SSLContext
类型的新对象,该对象表示与服务器名称匹配的证书链。由于 TLS 连接的早期协商阶段,只有有限的方法和属性可用,例如
SSLSocket.selected_alpn_protocol()
和SSLSocket.context
。SSLSocket.getpeercert()
、SSLSocket.get_verified_chain()
、SSLSocket.get_unverified_chain()
、SSLSocket.cipher()
和SSLSocket.compression()
方法要求 TLS 连接已超出 TLS 客户端 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_callback 与 sni_callback 类似,不同之处在于,当服务器主机名是 IDN 编码的国际化域名时,server_name_callback 接收解码的 U 标签 ("pythön.org"
)。如果服务器名称存在解码错误,则 TLS 连接将终止,并向客户端发送
ALERT_DESCRIPTION_INTERNAL_ERROR
致命 TLS 警报消息。在 3.4 版本中添加。
- SSLContext.load_dh_params(dhfile)¶
加载 Diffie-Hellman (DH) 密钥交换的密钥生成参数。使用 DH 密钥交换可以提高前向保密性,但会增加计算资源(服务器和客户端)。dhfile 参数应该是包含 PEM 格式的 DH 参数的文件的路径。
此设置不适用于客户端套接字。您还可以使用
OP_SINGLE_DH_USE
选项来进一步提高安全性。在 3.3 版本中添加。
- SSLContext.set_ecdh_curve(curve_name)¶
设置基于椭圆曲线的 Diffie-Hellman (ECDH) 密钥交换的曲线名称。ECDH 比常规 DH 快得多,并且可以说一样安全。curve_name 参数应该是一个字符串,描述一个众所周知的椭圆曲线,例如
prime256v1
表示广泛支持的曲线。此设置不适用于客户端套接字。您还可以使用
OP_SINGLE_ECDH_USE
选项来进一步提高安全性。如果
HAS_ECDH
为False
,则此方法不可用。在 3.3 版本中添加。
另请参见
- SSL/TLS 和完全前向保密
Vincent Bernat。
- SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=None, session=None)¶
包装现有的 Python 套接字 sock 并返回
SSLContext.sslsocket_class
的实例(默认为SSLSocket
)。返回的 SSL 套接字绑定到上下文、其设置和证书。sock 必须是SOCK_STREAM
套接字;不支持其他套接字类型。参数
server_side
是一个布尔值,用于标识此套接字需要服务器端行为还是客户端行为。对于客户端套接字,上下文构造是延迟的;如果底层套接字尚未连接,则上下文构造将在套接字上调用
connect()
后执行。对于服务器端套接字,如果套接字没有远程对等点,则假定它是一个侦听套接字,并且服务器端 SSL 包装会自动在通过accept()
方法接受的客户端连接上执行。该方法可能会引发SSLError
。在客户端连接上,可选参数 server_hostname 指定我们正在连接的服务的 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 对象 incoming 和 outgoing 并返回
SSLContext.sslobject_class
(默认为SSLObject
)的实例。SSL 例程将从传入的 BIO 读取输入数据,并将数据写入传出的 BIO。server_side、server_hostname 和 session 参数的含义与
SSLContext.wrap_socket()
中的含义相同。在 3.6 版本中变更: 添加了 session 参数。
在 3.7 版本中变更: 该方法返回
SSLContext.sslobject_class
的实例,而不是硬编码的SSLObject
。
- SSLContext.sslobject_class¶
SSLContext.wrap_bio()
的返回类型,默认为SSLObject
。可以在类的实例上重写该属性,以便返回SSLObject
的自定义子类。3.7 版本新增。
- SSLContext.session_stats()¶
获取有关此上下文创建或管理的 SSL 会话的统计信息。返回一个字典,其中将每个信息片段的名称映射到其数值。例如,这是自创建上下文以来会话缓存中的总命中数和未命中数
>>> stats = context.session_stats() >>> stats['hits'], stats['misses'] (0, 0)
- SSLContext.check_hostname¶
是否在
SSLSocket.do_handshake()
中匹配对等证书的主机名。上下文的verify_mode
必须设置为CERT_OPTIONAL
或CERT_REQUIRED
,并且您必须将 server_hostname 传递给wrap_socket()
才能匹配主机名。启用主机名检查会自动将verify_mode
从CERT_NONE
设置为CERT_REQUIRED
。只要启用主机名检查,就无法将其设置回CERT_NONE
。PROTOCOL_TLS_CLIENT
协议默认启用主机名检查。对于其他协议,必须显式启用主机名检查。示例
import socket, ssl context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) context.verify_mode = ssl.CERT_REQUIRED context.check_hostname = True context.load_default_certs() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com') ssl_sock.connect(('www.verisign.com', 443))
在 3.4 版本中添加。
在 3.7 版本中变更: 当启用主机名检查并且
verify_mode
为CERT_NONE
时,verify_mode
现在会自动更改为CERT_REQUIRED
。以前,相同的操作会因ValueError
而失败。
- SSLContext.keylog_filename¶
每当生成或接收密钥材料时,将 TLS 密钥写入密钥日志文件。密钥日志文件仅用于调试目的。文件格式由 NSS 指定,并被许多流量分析器(如 Wireshark)使用。日志文件以仅追加模式打开。写入在线程之间同步,但在进程之间不同步。
3.8 版本中新增。
- SSLContext.maximum_version¶
一个
TLSVersion
枚举成员,表示支持的最高 TLS 版本。该值默认为TLSVersion.MAXIMUM_SUPPORTED
。对于除PROTOCOL_TLS
、PROTOCOL_TLS_CLIENT
和PROTOCOL_TLS_SERVER
之外的协议,该属性是只读的。属性
maximum_version
,minimum_version
和SSLContext.options
都会影响上下文支持的 SSL 和 TLS 版本。实现不会阻止无效的组合。例如,options
中包含OP_NO_TLSv1_2
,且maximum_version
设置为TLSVersion.TLSv1_2
的上下文将无法建立 TLS 1.2 连接。3.7 版本新增。
- SSLContext.minimum_version¶
类似于
SSLContext.maximum_version
,但它是支持的最低版本,或者为TLSVersion.MINIMUM_SUPPORTED
。3.7 版本新增。
- SSLContext.num_tickets¶
控制
PROTOCOL_TLS_SERVER
上下文的 TLS 1.3 会话票证数量。此设置对 TLS 1.0 到 1.2 连接没有影响。3.8 版本中新增。
- SSLContext.options¶
一个整数,表示在此上下文中启用的 SSL 选项集。默认值为
OP_ALL
,但你可以通过对它们进行“或”运算来指定其他选项,例如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_version
和SSLContext.maximum_version
。
- SSLContext.post_handshake_auth¶
启用 TLS 1.3 握手后客户端身份验证。默认情况下禁用握手后身份验证,服务器只能在初始握手期间请求 TLS 客户端证书。启用后,服务器可以在握手后的任何时间请求 TLS 客户端证书。
在客户端套接字上启用时,客户端会向服务器发出信号,表明它支持握手后身份验证。
在服务器端套接字上启用时,
SSLContext.verify_mode
也必须设置为CERT_OPTIONAL
或CERT_REQUIRED
。实际的客户端证书交换将延迟到调用SSLSocket.verify_client_post_handshake()
并执行某些 I/O 操作之后。3.8 版本中新增。
- SSLContext.protocol¶
构造上下文时选择的协议版本。此属性为只读。
- SSLContext.hostname_checks_common_name¶
当缺少主题备用名称扩展时,
check_hostname
是否会回退到验证证书的主题通用名称(默认值:true)。3.7 版本新增。
在 3.10 版本中变更: 此标志在 1.1.1l 之前的 OpenSSL 版本中无效。Python 3.8.9、3.9.3 和 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_NONE
、CERT_OPTIONAL
或CERT_REQUIRED
之一。在 3.6 版本中变更:
SSLContext.verify_mode
返回VerifyMode
枚举>>> ssl.create_default_context().verify_mode <VerifyMode.CERT_REQUIRED: 2>
- SSLContext.set_psk_client_callback(callback)¶
在客户端连接上启用 TLS-PSK(预共享密钥)身份验证。
一般来说,应该优先选择基于证书的身份验证,而不是此方法。
参数
callback
是一个可调用对象,其签名如下:def callback(hint: str | None) -> tuple[str | None, bytes]
。hint
参数是服务器发送的可选身份提示。返回值是一个元组,格式为 (client-identity, psk)。Client-identity 是一个可选字符串,服务器可以使用它来选择客户端的相应 PSK。当使用 UTF-8 编码时,该字符串必须小于或等于256
个八位字节。PSK 是一个表示预共享密钥的 类字节对象。返回零长度 PSK 以拒绝连接。将
callback
设置为None
将删除任何现有的回调。用法示例
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) context.check_hostname = False context.verify_mode = ssl.CERT_NONE context.maximum_version = ssl.TLSVersion.TLSv1_2 context.set_ciphers('PSK') # A simple lambda: psk = bytes.fromhex('c0ffee') context.set_psk_client_callback(lambda hint: (None, psk)) # A table using the hint from the server: psk_table = { 'ServerId_1': bytes.fromhex('c0ffee'), 'ServerId_2': bytes.fromhex('facade') } def callback(hint): return 'ClientId_1', psk_table.get(hint, b'') context.set_psk_client_callback(callback)
如果
HAS_PSK
为False
,此方法将引发NotImplementedError
。在 3.13 版本中添加。
- SSLContext.set_psk_server_callback(callback, identity_hint=None)¶
在服务器端连接上启用 TLS-PSK(预共享密钥)身份验证。
一般来说,应该优先选择基于证书的身份验证,而不是此方法。
参数
callback
是一个可调用对象,其签名如下:def callback(identity: str | None) -> bytes
。identity
参数是客户端发送的可选身份标识,可用于选择相应的 PSK。返回值是一个类字节对象,表示预共享密钥。返回零长度的 PSK 将拒绝连接。将
callback
设置为None
将删除任何现有的回调。参数
identity_hint
是发送给客户端的可选身份提示字符串。当使用 UTF-8 编码时,该字符串必须小于或等于256
个八位字节。注解
当使用 TLS 1.3 时,
identity_hint
参数不会发送给客户端。用法示例
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.maximum_version = ssl.TLSVersion.TLSv1_2 context.set_ciphers('PSK') # A simple lambda: psk = bytes.fromhex('c0ffee') context.set_psk_server_callback(lambda identity: psk) # A table using the identity of the client: psk_table = { 'ClientId_1': bytes.fromhex('c0ffee'), 'ClientId_2': bytes.fromhex('facade') } def callback(identity): return psk_table.get(identity, b'') context.set_psk_server_callback(callback, 'ServerId_1')
如果
HAS_PSK
为False
,此方法将引发NotImplementedError
。在 3.13 版本中添加。
证书¶
通常,证书是公钥/私钥系统的一部分。在这个系统中,每个主体(可能是机器、个人或组织)都被分配一个唯一的两部分加密密钥。密钥的一部分是公开的,称为公钥;另一部分是保密的,称为私钥。这两部分是相关的,如果使用其中一部分加密消息,则可以使用另一部分解密,而且只能使用另一部分解密。
证书包含关于两个主体的信息。它包含一个主题的名称和主题的公钥。它还包含第二个主体(颁发者)的声明,表明该主题是其声称的身份,并且这确实是该主题的公钥。颁发者的声明使用颁发者的私钥签名,只有颁发者知道该私钥。但是,任何人都可以通过查找颁发者的公钥、使用该公钥解密该声明并将其与证书中的其他信息进行比较来验证颁发者的声明。证书还包含关于其有效时间段的信息。这表示为两个字段,称为“notBefore”和“notAfter”。
在 Python 中使用证书时,客户端或服务器可以使用证书来证明自己的身份。还可以要求网络连接的另一端提供证书,并且该证书可以被验证,以使需要此类验证的客户端或服务器满意。如果验证失败,连接尝试可以设置为引发异常。验证由底层的 OpenSSL 框架自动完成;应用程序无需关心其机制。但是,应用程序通常需要提供证书集,以允许此过程发生。
Python 使用文件来包含证书。它们应格式化为“PEM”(请参阅RFC 1422),这是一种 base-64 编码形式,并用标题行和页脚行包裹。
-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----
证书链¶
包含证书的 Python 文件可以包含一系列证书,有时称为证书链。此链应从“是”客户端或服务器的主体的特定证书开始,然后是该证书的颁发者的证书,然后是该证书的颁发者的证书,依此类推,直到获得一个自签名的证书,即具有相同主题和颁发者的证书,有时称为根证书。这些证书应该只是在证书文件中连接在一起。例如,假设我们有一个三证书链,从我们的服务器证书到签署我们服务器证书的证书颁发机构的证书,再到颁发证书颁发机构证书的机构的根证书。
-----BEGIN CERTIFICATE-----
... (certificate for your server)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the certificate for the CA)...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
... (the root certificate for the CA's issuer)...
-----END CERTIFICATE-----
CA 证书¶
如果要要求验证连接另一端的证书,则需要提供一个“CA 证书”文件,其中填充了您愿意信任的每个颁发者的证书链。同样,此文件只是包含这些连接在一起的链。对于验证,Python 将使用文件中找到的第一个匹配链。可以通过调用SSLContext.load_default_certs()
来使用平台的证书文件,这可以通过create_default_context()
自动完成。
组合密钥和证书¶
通常,私钥与证书存储在同一个文件中;在这种情况下,只需传递SSLContext.load_cert_chain()
的 certfile
参数。如果私钥与证书一起存储,则它应该位于证书链中的第一个证书之前。
-----BEGIN RSA PRIVATE KEY-----
... (private key in base64 encoding) ...
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----
自签名证书¶
如果要创建一个提供 SSL 加密连接服务的服务器,则需要为该服务获取证书。有许多方法可以获取适当的证书,例如从证书颁发机构购买一个。另一种常见做法是生成自签名证书。最简单的方法是使用 OpenSSL 包,使用类似以下内容的方法:
% openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem
Generating a 1024 bit RSA private key
.......++++++
.............................++++++
writing new private key to 'cert.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:MyState
Locality Name (eg, city) []:Some City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc.
Organizational Unit Name (eg, section) []:My Group
Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com
Email Address []:ops@myserver.mygroup.myorganization.com
%
自签名证书的缺点是它是自己的根证书,而且没有其他人会在其已知(和受信任)的根证书缓存中拥有它。
示例¶
测试 SSL 支持¶
要测试 Python 安装中是否存在 SSL 支持,用户代码应使用以下习惯用法:
try:
import ssl
except ImportError:
pass
else:
... # do something that requires SSL support
客户端操作¶
此示例使用推荐的客户端套接字安全设置(包括自动证书验证)创建一个 SSL 上下文:
>>> context = ssl.create_default_context()
如果您希望自己调整安全设置,则可以从头开始创建一个上下文(但请注意,您可能无法获得正确的设置):
>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
(此代码段假定您的操作系统将所有 CA 证书的捆绑包放在 /etc/ssl/certs/ca-bundle.crt
中;如果不是,您将收到一个错误,并且必须调整位置)
PROTOCOL_TLS_CLIENT
协议配置上下文以进行证书验证和主机名验证。verify_mode
设置为 CERT_REQUIRED
,check_hostname
设置为 True
。所有其他协议都使用不安全的默认值创建 SSL 上下文。
当您使用上下文连接到服务器时,CERT_REQUIRED
和 check_hostname
验证服务器证书:它确保服务器证书已使用其中一个 CA 证书签名,检查签名的正确性,并验证其他属性,例如主机名的有效性和身份。
>>> conn = context.wrap_socket(socket.socket(socket.AF_INET),
... server_hostname="www.python.org")
>>> conn.connect(("www.python.org", 443))
然后,您可以获取证书:
>>> cert = conn.getpeercert()
目视检查表明该证书确实标识了所需的服务(即 HTTPS 主机 www.python.org
):
>>> pprint.pprint(cert)
{'OCSP': ('http://ocsp.digicert.com',),
'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt',),
'crlDistributionPoints': ('http://crl3.digicert.com/sha2-ev-server-g1.crl',
'http://crl4.digicert.com/sha2-ev-server-g1.crl'),
'issuer': ((('countryName', 'US'),),
(('organizationName', 'DigiCert Inc'),),
(('organizationalUnitName', 'www.digicert.com'),),
(('commonName', 'DigiCert SHA2 Extended Validation Server CA'),)),
'notAfter': 'Sep 9 12:00:00 2016 GMT',
'notBefore': 'Sep 5 00:00:00 2014 GMT',
'serialNumber': '01BB6F00122B177F36CAB49CEA8B6B26',
'subject': ((('businessCategory', 'Private Organization'),),
(('1.3.6.1.4.1.311.60.2.1.3', 'US'),),
(('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),),
(('serialNumber', '3359300'),),
(('streetAddress', '16 Allen Rd'),),
(('postalCode', '03894-4801'),),
(('countryName', 'US'),),
(('stateOrProvinceName', 'NH'),),
(('localityName', 'Wolfeboro'),),
(('organizationName', 'Python Software Foundation'),),
(('commonName', 'www.python.org'),)),
'subjectAltName': (('DNS', 'www.python.org'),
('DNS', 'python.org'),
('DNS', 'pypi.org'),
('DNS', 'docs.python.org'),
('DNS', 'testpypi.org'),
('DNS', 'bugs.python.org'),
('DNS', 'wiki.python.org'),
('DNS', 'hg.python.org'),
('DNS', 'mail.python.org'),
('DNS', 'packaging.python.org'),
('DNS', 'pythonhosted.org'),
('DNS', 'www.pythonhosted.org'),
('DNS', 'test.pythonhosted.org'),
('DNS', 'us.pycon.org'),
('DNS', 'id.python.org')),
'version': 3}
现在,SSL 通道已建立并且证书已验证,您可以继续与服务器通信:
>>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
>>> pprint.pprint(conn.recv(1024).split(b"\r\n"))
[b'HTTP/1.1 200 OK',
b'Date: Sat, 18 Oct 2014 18:27:20 GMT',
b'Server: nginx',
b'Content-Type: text/html; charset=utf-8',
b'X-Frame-Options: SAMEORIGIN',
b'Content-Length: 45679',
b'Accept-Ranges: bytes',
b'Via: 1.1 varnish',
b'Age: 2188',
b'X-Served-By: cache-lcy1134-LCY',
b'X-Cache: HIT',
b'X-Cache-Hits: 11',
b'Vary: Cookie',
b'Strict-Transport-Security: max-age=63072000; includeSubDomains',
b'Connection: close',
b'',
b'']
请参阅下面的安全注意事项的讨论。
服务器端操作¶
对于服务器操作,通常您需要具有服务器证书和私钥,每个都在一个文件中。您将首先创建一个包含密钥和证书的上下文,以便客户端可以检查您的真实性。然后,您将打开一个套接字,将其绑定到一个端口,对其调用 listen()
,并开始等待客户端连接:
import socket, ssl
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
bindsocket = socket.socket()
bindsocket.bind(('myaddr.example.com', 10023))
bindsocket.listen(5)
当客户端连接时,您将对套接字调用 accept()
以从另一端获取新套接字,并使用上下文的 SSLContext.wrap_socket()
方法为连接创建服务器端 SSL 套接字:
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
然后,您将从 connstream
中读取数据并对其执行某些操作,直到您完成客户端的操作(或客户端完成您的操作):
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
if not do_something(connstream, data):
# we'll assume do_something returns False
# when we're finished with client
break
data = connstream.recv(1024)
# finished with client
然后回到监听新客户端连接(当然,真正的服务器可能会在单独的线程中处理每个客户端连接,或者将套接字置于 非阻塞模式 并使用事件循环)。
关于非阻塞套接字的说明¶
SSL 套接字在非阻塞模式下的行为与常规套接字略有不同。当使用非阻塞套接字时,您需要注意以下几点:
如果 I/O 操作将阻塞,则大多数
SSLSocket
方法将引发SSLWantWriteError
或SSLWantReadError
,而不是BlockingIOError
。如果底层套接字上需要读取操作,则会引发SSLWantReadError
;对于底层套接字上的写入操作,则会引发SSLWantWriteError
。请注意,尝试写入 SSL 套接字可能需要首先从底层套接字读取,而尝试从 SSL 套接字读取可能需要事先对底层套接字进行写入。在 3.5 版本中更改: 在较早的 Python 版本中,
SSLSocket.send()
方法返回零,而不是引发SSLWantWriteError
或SSLWantReadError
。调用
select()
会告知您操作系统级别的套接字是否可以读取(或写入),但这并不意味着上层 SSL 层有足够的数据。 例如,可能只到达了 SSL 帧的一部分。 因此,您必须准备好处理SSLSocket.recv()
和SSLSocket.send()
失败的情况,并在再次调用select()
后重试。相反,由于 SSL 层有自己的成帧机制,SSL 套接字可能仍然有可用于读取的数据,而
select()
并不知道。 因此,您应该首先调用SSLSocket.recv()
来耗尽任何潜在可用的数据,然后仅在仍然需要时阻塞在select()
调用上。SSL 握手本身将是非阻塞的:必须重试
SSLSocket.do_handshake()
方法,直到它成功返回。以下是使用select()
等待套接字就绪的概要while True: try: sock.do_handshake() break except ssl.SSLWantReadError: select.select([sock], [], []) except ssl.SSLWantWriteError: select.select([], [sock], [])
另请参见
asyncio
模块支持 非阻塞 SSL 套接字 并提供更高级别的 Streams API。 它使用 selectors
模块轮询事件,并处理 SSLWantWriteError
、SSLWantReadError
和 BlockingIOError
异常。 它也异步运行 SSL 握手。
内存 BIO 支持¶
在 3.5 版本中添加。
自从 Python 2.6 中引入 SSL 模块以来,SSLSocket
类提供了两个相关但不同的功能领域
SSL 协议处理
网络 IO
网络 IO API 与 socket.socket
提供的 API 相同,SSLSocket
也从中继承。 这允许将 SSL 套接字用作常规套接字的直接替换,从而使向现有应用程序添加 SSL 支持非常容易。
组合 SSL 协议处理和网络 IO 通常可以很好地工作,但在某些情况下却不行。 一个示例是异步 IO 框架,它们希望使用与 socket.socket
和内部 OpenSSL 套接字 IO 例程所假设的“基于文件描述符的 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
相关的重要说明对
SSLObject
的所有 IO 都是 非阻塞的。 这意味着例如,如果read()
需要比传入 BIO 可用的更多数据,则会引发SSLWantReadError
。
在版本 3.7 中更改:
SSLObject
实例必须使用wrap_bio()
创建。 在早期版本中,可以直接创建实例。 这从未被记录或正式支持过。
SSLObject 使用内存缓冲区与外部世界通信。 类 MemoryBIO
提供了一个可用于此目的的内存缓冲区。 它包装了一个 OpenSSL 内存 BIO(基本 IO)对象
SSL 会话¶
在版本 3.6 中添加。
安全注意事项¶
最佳默认值¶
对于客户端使用,如果您的安全策略没有任何特殊要求,强烈建议您使用 create_default_context()
函数来创建您的 SSL 上下文。它将加载系统受信任的 CA 证书,启用证书验证和主机名检查,并尝试选择合理安全的协议和密码设置。
例如,以下是如何使用 smtplib.SMTP
类来创建与 SMTP 服务器的受信任的安全连接的示例
>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')
如果连接需要客户端证书,可以使用 SSLContext.load_cert_chain()
添加。
相比之下,如果您通过自己调用 SSLContext
构造函数来创建 SSL 上下文,则默认情况下不会启用证书验证和主机名检查。如果您这样做,请阅读以下段落以达到良好的安全级别。
手动设置¶
验证证书¶
直接调用 SSLContext
构造函数时,默认值为 CERT_NONE
。由于它不验证对方的身份,因此可能是不安全的,尤其是在客户端模式下,您通常希望确保您正在与之通信的服务器的真实性。因此,在客户端模式下,强烈建议使用 CERT_REQUIRED
。但是,它本身是不够的;您还必须检查通过调用 SSLSocket.getpeercert()
获取的服务器证书是否与所需的服务匹配。对于许多协议和应用程序,可以通过主机名识别服务。当启用 SSLContext.check_hostname
时,会自动执行此常用检查。
在 3.7 版本中更改: 主机名匹配现在由 OpenSSL 执行。Python 不再使用 match_hostname()
。
在服务器模式下,如果您想使用 SSL 层对客户端进行身份验证(而不是使用更高级别的身份验证机制),您还必须指定 CERT_REQUIRED
并类似地检查客户端证书。
协议版本¶
SSL 版本 2 和 3 被认为是不安全的,因此使用它们是危险的。如果想要客户端和服务器之间的最大兼容性,建议使用 PROTOCOL_TLS_CLIENT
或 PROTOCOL_TLS_SERVER
作为协议版本。默认情况下禁用 SSLv2 和 SSLv3。
>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.minimum_version = ssl.TLSVersion.TLSv1_3
>>> client_context.maximum_version = ssl.TLSVersion.TLSv1_3
上面创建的 SSL 上下文将仅允许 TLSv1.3 及更高版本(如果您的系统支持)连接到服务器。PROTOCOL_TLS_CLIENT
默认情况下意味着证书验证和主机名检查。您必须将证书加载到上下文中。
密码选择¶
如果您有高级安全要求,可以通过 SSLContext.set_ciphers()
方法微调协商 SSL 会话时启用的密码。从 Python 3.2.3 开始,ssl 模块默认禁用某些弱密码,但您可能希望进一步限制密码选择。请务必阅读 OpenSSL 关于 密码列表格式 的文档。如果要检查给定密码列表启用了哪些密码,请使用 SSLContext.get_ciphers()
或系统上的 openssl ciphers
命令。
多进程¶
如果在多进程应用程序中使用此模块(例如,使用 multiprocessing
或 concurrent.futures
模块),请注意 OpenSSL 的内部随机数生成器不能正确处理分叉的进程。如果应用程序使用任何带有 os.fork()
的 SSL 功能,则必须更改父进程的 PRNG 状态。任何成功调用 RAND_add()
或 RAND_bytes()
就足够了。
TLS 1.3¶
3.7 版本新增。
TLS 1.3 协议的行为与以前版本的 TLS/SSL 略有不同。一些新的 TLS 1.3 功能尚未可用。
TLS 1.3 使用不相交的密码套件集。默认情况下启用所有 AES-GCM 和 ChaCha20 密码套件。方法
SSLContext.set_ciphers()
尚不能启用或禁用任何 TLS 1.3 密码,但SSLContext.get_ciphers()
会返回它们。会话票证不再作为初始握手的一部分发送,并且处理方式不同。
SSLSocket.session
和SSLSession
与 TLS 1.3 不兼容。客户端证书也不再在初始握手期间进行验证。服务器可以随时请求证书。当客户端从服务器发送或接收应用程序数据时,它们会处理证书请求。
尚不支持 TLS 1.3 功能,如早期数据、延迟 TLS 客户端证书请求、签名算法配置和重新密钥。
另请参见
- 类
socket.socket
底层
socket
类的文档- SSL/TLS 强加密:简介
来自 Apache HTTP 服务器文档的介绍
- RFC 1422: 互联网电子邮件的隐私增强:第二部分:基于证书的密钥管理
Steve Kent
- RFC 4086: 安全随机性要求
Donald E., Jeffrey I. Schiller
- RFC 5280: 互联网 X.509 公钥基础设施证书和证书吊销列表 (CRL) 配置文件
D. Cooper
- RFC 5246: 传输层安全 (TLS) 协议版本 1.2
T. Dierks 等人。
- RFC 6066:传输层安全(TLS)扩展
D. Eastlake
- IANA TLS:传输层安全(TLS)参数
IANA
- RFC 7525:关于安全使用传输层安全(TLS)和数据报传输层安全(DTLS)的建议
IETF
- Mozilla 的服务器端 TLS 建议
Mozilla