ssl — 套接字对象的 TLS/SSL 包装器 — Python 文档
ssl — 套接字对象的 TLS/SSL 包装器
源代码: :source:`Lib/ssl.py`
该模块为客户端和服务器端的网络套接字提供对传输层安全性(通常称为“安全套接字层”)加密和对等身份验证设施的访问。 该模块使用 OpenSSL 库。 它在所有现代 Unix 系统、Windows、Mac OS X 和可能的其他平台上都可用,只要在该平台上安装了 OpenSSL。
笔记
某些行为可能取决于平台,因为调用的是操作系统套接字 API。 已安装的 OpenSSL 版本也可能导致行为发生变化。 例如,TLSv1.1 和 TLSv1.2 带有 openssl 版本 1.0.1。
本节记录了 ssl
模块中的对象和函数; 有关 TLS、SSL 和证书的更多一般信息,请参阅底部“另请参阅”部分中的文档。
该模块提供了一个类,ssl.SSLSocket,它派生自 socket.socket 类型,并提供了一个类似套接字的包装器,它也对通过套接字的数据进行加密和解密使用 SSL。 它支持其他方法,例如 getpeercert()
,它检索连接另一端的证书,以及 cipher()
,它检索用于安全连接的密码。
对于更复杂的应用程序,ssl.SSLContext 类有助于管理设置和证书,然后可以由通过 SSLContext.wrap_socket() 方法创建的 SSL 套接字继承。
3.5.3 版更改: 更新以支持与 OpenSSL 1.1.0 的链接
3.6 版更改:OpenSSL 0.9.8、1.0.0 和 1.0.1 已弃用且不再受支持。 将来 ssl 模块至少需要 OpenSSL 1.0.2 或 1.1.0。
函数、常量和异常
套接字创建
从 Python 3.2 和 2.7.9 开始,建议使用 SSLContext 实例的 SSLContext.wrap_socket() 将套接字包装为 SSLSocket 对象。 辅助函数 create_default_context() 返回一个具有安全默认设置的新上下文。 旧的 wrap_socket() 函数已被弃用,因为它既低效又不支持服务器名称指示 (SNI) 和主机名匹配。
具有默认上下文和 IPv4/IPv6 双栈的客户端套接字示例:
import socket
import ssl
hostname = 'www.python.org'
context = ssl.create_default_context()
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
具有自定义上下文和 IPv4 的客户端套接字示例:
hostname = 'www.python.org'
# PROTOCOL_TLS_CLIENT requires valid cert chain and hostname
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations('path/to/cabundle.pem')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
print(ssock.version())
侦听本地主机 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 对象,其中包含给定 用途 的默认设置。 这些设置由 ssl 模块选择,通常比直接调用 SSLContext 构造函数时代表更高的安全级别。
cafile、capath、cadata 代表可选的 CA 证书以信任证书验证,如 SSLContext.load_verify_locations()。 如果三个都是None,这个函数可以选择信任系统默认的CA证书。
设置为:PROTOCOL_TLS、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() 启用键记录。笔记
协议、选项、密码和其他设置可能随时更改为更具限制性的值,而无需事先弃用。 这些值代表了兼容性和安全性之间的公平平衡。
如果您的应用程序需要特定设置,您应该创建一个 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
3.4 版中的新功能。
在 3.4.4 版更改:RC4 从默认密码字符串中删除。
3.6 版更改:ChaCha20/Poly1305 被添加到默认密码字符串中。
3DES 已从默认密码字符串中删除。
3.8 版更改: 增加了对
SSLKEYLOGFILE
按键记录的支持。
例外
- 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
尝试读取或写入时引发 SSLError 的子类并且 SSL 连接已完全关闭。 请注意,这并不意味着底层传输(读取 TCP)已关闭。
3.3 版中的新功能。
- exception ssl.SSLWantReadError
SSLError 的子类在尝试读取或写入数据时由 非阻塞 SSL 套接字 引发,但需要在底层 TCP 传输上接收更多数据才能满足请求.
3.3 版中的新功能。
- exception ssl.SSLWantWriteError
SSLError 的子类在尝试读取或写入数据时由 非阻塞 SSL 套接字 引发,但需要在底层 TCP 传输上发送更多数据才能满足请求.
3.3 版中的新功能。
- exception ssl.SSLSyscallError
SSLError 的子类在尝试对 SSL 套接字执行操作时遇到系统错误时引发。 不幸的是,没有简单的方法可以检查原始 errno 编号。
3.3 版中的新功能。
- exception ssl.SSLEOFError
当 SSL 连接突然终止时,会引发 SSLError 的子类。 通常,您不应在遇到此错误时尝试重用底层传输。
3.3 版中的新功能。
- exception ssl.SSLCertVerificationError
证书验证失败时引发 SSLError 的子类。
3.7 版中的新功能。
- verify_code
表示验证错误的数字错误编号。
- verify_message
验证错误的人类可读字符串。
- exception ssl.CertificateError
-
3.7 版更改: 异常现在是 SSLCertVerificationError 的别名。
随机生成
- ssl.RAND_bytes(num)
返回 num 加密强伪随机字节。 如果 PRNG 没有用足够的数据播种或者当前 RAND 方法不支持该操作,则引发 SSLError。 RAND_status() 可用于检查 PRNG 的状态,RAND_add() 可用于播种 PRNG。
对于几乎所有应用程序 os.urandom() 是首选。
阅读维基百科文章 加密安全伪随机数生成器 (CSPRNG),了解加密生成器的要求。
3.3 版中的新功能。
- ssl.RAND_pseudo_bytes(num)
返回(字节,is_cryptographic):字节是num个伪随机字节,如果生成的字节是加密强的,is_cryptographic是
True
。 如果当前 RAND 方法不支持该操作,则引发 SSLError。如果生成的伪随机字节序列足够长,则它们将是唯一的,但不一定是不可预测的。 它们可用于非加密目的和加密协议中的某些目的,但通常不用于密钥生成等。
对于几乎所有应用程序 os.urandom() 是首选。
3.3 版中的新功能。
自 3.6 版起已弃用:OpenSSL 已弃用 ssl.RAND_pseudo_bytes(),请改用 ssl.RAND_bytes()。
- ssl.RAND_status()
- 如果 SSL 伪随机数生成器具有“足够”的随机性,则返回
True
,否则返回False
。 您可以使用 ssl.RAND_egd() 和 ssl.RAND_add() 来增加伪随机数生成器的随机性。
- ssl.RAND_egd(path)
如果您在某处运行熵收集守护进程 (EGD),并且 path 是对其打开的套接字连接的路径名,这将从套接字读取 256 字节的随机性,并将其添加到 SSL伪随机数生成器,以增加生成的密钥的安全性。 这通常只在没有更好随机源的系统上才有必要。
有关熵收集守护进程的来源,请参阅 http://egd.sourceforge.net/ 或 http://prngd.sourceforge.net/。
- ssl.RAND_add(bytes, entropy)
将给定的 bytes 混合到 SSL 伪随机数生成器中。 参数 entropy(浮点数)是包含在字符串中的熵的下限(因此您始终可以使用
0.0
)。 有关熵源的更多信息,请参阅 RFC 1750。3.5 版更改:现在接受 可写 字节类对象 。
证书办理
- ssl.match_hostname(cert, hostname)
验证 cert(以 SSLSocket.getpeercert() 返回的解码格式)与给定的 主机名 匹配。 应用的规则是用于检查 HTTPS 服务器身份的规则,如 RFC 2818、RFC 5280 和 ]RFC 6125。 除HTTPS外,此功能应适用于在各种基于SSL的协议(如FTPS、IMAPS、POPS等)中检查服务器的身份。
CertificateError 在失败时引发。 成功时,该函数不返回任何内容:
>>> cert = {'subject': ((('commonName', 'example.com'),),)} >>> ssl.match_hostname(cert, "example.com") >>> ssl.match_hostname(cert, "example.org") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/py3k/Lib/ssl.py", line 130, in match_hostname ssl.CertificateError: hostname 'example.org' doesn't match 'example.com'
3.2 版中的新功能。
在 3.3.3 版更改:该函数现在遵循 RFC 6125,第 6.4.3 节,并且不匹配多个通配符(例如
*.*.com
或*a*.example.org
) 或国际化域名 (IDN) 片段中的通配符。www*.xn--pthon-kva.org
等 IDN A 标签仍受支持,但x*.python.org
不再匹配xn--tda.python.org
。在 3.5 版中更改:现在支持 IP 地址匹配(如果出现在证书的 subjectAltName 字段中)。
3.7 版本变化: 该功能不再用于 TLS 连接。 主机名匹配现在由 OpenSSL 执行。
当它是该段中的最左边和唯一的字符时允许通配符。 不再支持像
www*.example.com
这样的部分通配符。自 3.7 版起已弃用。
- ssl.cert_time_to_seconds(cert_time)
返回自纪元以来的时间(以秒为单位),给定
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, ca_certs=None)
给定受 SSL 保护的服务器的地址
addr
,作为 (hostname, port-number) 对,获取服务器的证书,并将其作为 PEM 返回- 编码的字符串。 如果指定了ssl_version
,则使用该版本的 SSL 协议尝试连接到服务器。 如果指定了ca_certs
,它应该是一个包含根证书列表的文件,格式与 SSLContext.wrap_socket() 中相同参数的格式相同。 该调用将尝试根据该组根证书验证服务器证书,如果验证尝试失败,则会失败。3.3 版变更:此功能现已兼容 IPv6。
3.5 版更改: 默认 ssl_version 从 PROTOCOL_SSLv3 更改为 PROTOCOL_TLS,以最大程度地与现代服务器兼容。
- ssl.DER_cert_to_PEM_cert(DER_cert_bytes)
- 将证书作为 DER 编码的字节块,返回相同证书的 PEM 编码字符串版本。
- ssl.PEM_cert_to_DER_cert(PEM_cert_string)
- 给定一个 ASCII PEM 字符串形式的证书,返回同一证书的 DER 编码字节序列。
- ssl.get_default_verify_paths()
返回一个命名元组,其中包含 OpenSSL 的默认 cafile 和 capath 的路径。 路径与 SSLContext.set_default_verify_paths() 使用的路径相同。 返回值是一个名为 的元组
DefaultVerifyPaths
:cafile
- 如果文件不存在,则解析到 cafile 或None
的路径,capath
- 如果目录不存在,则解析到 capath 或None
的路径,openssl_cafile_env
- 指向 cafile 的 OpenSSL 环境密钥,openssl_cafile
- cafile 的硬编码路径,openssl_capath_env
- OpenSSL 的环境密钥,指向一个 capath,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 将证书的用途指定为一组 OIDS 或完全True
,如果证书对于所有用途都值得信赖。例子:
>>> ssl.enum_certificates("CA") [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}), (b'data...', 'x509_asn', True)]
3.4 版中的新功能。
- ssl.enum_crls(store_name)
从 Windows 的系统证书存储中检索 CRL。 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
。3.4 版中的新功能。
- ssl.wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_TLS, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None)
获取 socket.socket 的实例
sock
,并返回 ssl.SSLSocket 的实例,socket.socket 的子类型,它包装SSL 上下文中的底层套接字。sock
必须是 SOCK_STREAM 套接字; 不支持其他套接字类型。在内部,函数创建一个 SSLContext,协议 ssl_version 和 SSLContext.options 设置为 cert_reqs。 如果设置了参数 keyfile、certfile、ca_certs 或 ciphers,则将这些值传递给 SSLContext.load_cert_chain() 、SSLContext.load_verify_locations() 和 SSLContext.set_ciphers()。
参数 server_side、do_handshake_on_connect 和 suppress_ragged_eofs 与 SSLContext.wrap_socket() 具有相同的含义。
自 3.7 版起已弃用: 自 Python 3.2 和 2.7.9 起,建议使用 SSLContext.wrap_socket() 而不是 wrap_socket()。 顶级功能是有限的,它会创建一个不安全的客户端套接字,而没有服务器名称指示或主机名匹配。
常数
所有常量现在都是 enum.IntEnum 或 enum.IntFlag 集合。
3.6 版中的新功能。
- ssl.CERT_NONE
SSLContext.verify_mode 的可能值,或 wrap_socket() 的
cert_reqs
参数。 除 PROTOCOL_TLS_CLIENT 外,均为默认模式。 使用客户端套接字,几乎可以接受任何证书。 验证错误,例如不受信任或过期的证书,将被忽略并且不会中止 TLS/SSL 握手。在服务器模式下,客户端不会请求任何证书,因此客户端不会发送任何用于客户端证书身份验证的证书。
请参阅下面对 安全注意事项 的讨论。
- ssl.CERT_OPTIONAL
SSLContext.verify_mode 的可能值,或 wrap_socket() 的
cert_reqs
参数。 在客户端模式下,CERT_OPTIONAL 与 CERT_REQUIRED 的含义相同。 建议将 CERT_REQUIRED 用于客户端套接字。在服务器模式下,客户端证书请求被发送到客户端。 客户端可以忽略请求或发送证书以执行 TLS 客户端证书身份验证。 如果客户端选择发送证书,则会对其进行验证。 任何验证错误都会立即中止 TLS 握手。
使用此设置需要将一组有效的 CA 证书传递给 SSLContext.load_verify_locations() 或作为 wrap_socket() 的
ca_certs
参数的值]。
- ssl.CERT_REQUIRED
SSLContext.verify_mode 的可能值,或 wrap_socket() 的
cert_reqs
参数。 在这种模式下,需要套接字连接另一端的证书; 如果未提供证书或验证失败,则会引发 SSLError。 此模式 不 足以验证客户端模式下的证书,因为它与主机名不匹配。 check_hostname 也必须启用以验证证书的真实性。 PROTOCOL_TLS_CLIENT 使用 CERT_REQUIRED 并默认启用 check_hostname。使用服务器套接字,此模式提供强制性 TLS 客户端证书身份验证。 客户端证书请求被发送到客户端,并且客户端必须提供有效且受信任的证书。
使用此设置需要将一组有效的 CA 证书传递给 SSLContext.load_verify_locations() 或作为 wrap_socket() 的
ca_certs
参数的值]。
- class ssl.VerifyMode
enum.IntEnum CERT_* 常量集合。
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_X509_TRUSTED_FIRST
SSLContext.verify_flags 的可能值。 它指示 OpenSSL 在构建信任链以验证证书时更喜欢受信任的证书。 默认情况下启用此标志。
版本 3.4.4 中的新功能。
- class ssl.VerifyFlags
enum.IntFlag VERIFY_* 常量的集合。
3.6 版中的新功能。
- ssl.PROTOCOL_TLS
选择客户端和服务器都支持的最高协议版本。 尽管名称如此,但此选项可以同时选择“SSL”和“TLS”协议。
3.6 版中的新功能。
- ssl.PROTOCOL_TLS_CLIENT
自动协商最高协议版本,如 PROTOCOL_TLS,但仅支持客户端 SSLSocket 连接。 该协议默认启用 CERT_REQUIRED 和 check_hostname。
3.6 版中的新功能。
- ssl.PROTOCOL_TLS_SERVER
自动协商最高协议版本,如 PROTOCOL_TLS,但仅支持服务器端 SSLSocket 连接。
3.6 版中的新功能。
- ssl.PROTOCOL_SSLv23
PROTOCOL_TLS 的别名。
自 3.6 版起已弃用: 改用 PROTOCOL_TLS。
- ssl.PROTOCOL_SSLv2
选择 SSL 版本 2 作为通道加密协议。
如果使用
OPENSSL_NO_SSL2
标志编译 OpenSSL,则此协议不可用。警告
SSL 版本 2 不安全。 非常不鼓励使用它。
自 3.6 版起已弃用:OpenSSL 已删除对 SSLv2 的支持。
- ssl.PROTOCOL_SSLv3
选择 SSL 版本 3 作为通道加密协议。
如果使用
OPENSSL_NO_SSLv3
标志编译 OpenSSL,则此协议不可用。警告
SSL 版本 3 不安全。 非常不鼓励使用它。
自 3.6 版起已弃用:OpenSSL 已弃用所有特定于版本的协议。 使用带有 OP_NO_SSLv3 等标志的默认协议 PROTOCOL_TLS。
- ssl.PROTOCOL_TLSv1
选择 TLS 版本 1.0 作为通道加密协议。
自 3.6 版起已弃用:OpenSSL 已弃用所有特定于版本的协议。 使用带有 OP_NO_SSLv3 等标志的默认协议 PROTOCOL_TLS。
- ssl.PROTOCOL_TLSv1_1
选择 TLS 1.1 版作为通道加密协议。 仅适用于 openssl 版本 1.0.1+。
3.4 版中的新功能。
自 3.6 版起已弃用:OpenSSL 已弃用所有特定于版本的协议。 使用带有 OP_NO_SSLv3 等标志的默认协议 PROTOCOL_TLS。
- ssl.PROTOCOL_TLSv1_2
选择 TLS 1.2 版作为通道加密协议。 这是最现代的版本,如果双方都可以说的话,这可能是最大程度保护的最佳选择。 仅适用于 openssl 版本 1.0.1+。
3.4 版中的新功能。
自 3.6 版起已弃用:OpenSSL 已弃用所有特定于版本的协议。 使用带有 OP_NO_SSLv3 等标志的默认协议 PROTOCOL_TLS。
- 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.7 版中的新功能。
自 3.7 版起已弃用: 该选项自 OpenSSL 1.1.0 起已弃用。 它被添加到 2.7.15、3.6.3 和 3.7.0 以向后兼容 OpenSSL 1.0.2。
- ssl.OP_NO_RENEGOTIATION
在 TLSv1.2 及更早版本中禁用所有重新协商。 不要发送 HelloRequest 消息,并忽略通过 ClientHello 的重新协商请求。
此选项仅适用于 OpenSSL 1.1.0h 及更高版本。
3.7 版中的新功能。
- ssl.OP_CIPHER_SERVER_PREFERENCE
使用服务器的密码排序首选项,而不是客户端的。 此选项对客户端套接字和 SSLv2 服务器套接字没有影响。
3.3 版中的新功能。
- ssl.OP_SINGLE_DH_USE
防止对不同的 SSL 会话重复使用相同的 DH 密钥。 这提高了前向保密性,但需要更多的计算资源。 此选项仅适用于服务器套接字。
3.3 版中的新功能。
- ssl.OP_SINGLE_ECDH_USE
防止对不同的 SSL 会话重复使用相同的 ECDH 密钥。 这提高了前向保密性,但需要更多的计算资源。 此选项仅适用于服务器套接字。
3.3 版中的新功能。
- ssl.OP_ENABLE_MIDDLEBOX_COMPAT
在 TLS 1.3 握手中发送虚拟更改密码规范 (CCS) 消息,使 TLS 1.3 连接看起来更像 TLS 1.2 连接。
此选项仅适用于 OpenSSL 1.1.1 及更高版本。
3.8 版中的新功能。
- ssl.OP_NO_COMPRESSION
禁用 SSL 通道上的压缩。 如果应用程序协议支持其自己的压缩方案,这将非常有用。
此选项仅适用于 OpenSSL 1.0.0 及更高版本。
3.3 版中的新功能。
- class ssl.Options
- enum.IntFlag OP_* 常量的集合。
- ssl.OP_NO_TICKET
防止客户端请求会话票证。
3.6 版中的新功能。
- ssl.OP_IGNORE_UNEXPECTED_EOF
忽略 TLS 连接的意外关闭。
此选项仅适用于 OpenSSL 3.0.0 及更高版本。
3.10 版中的新功能。
- 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 密钥交换。 除非分发者明确禁用该功能,否则这应该是正确的。
3.3 版中的新功能。
- ssl.HAS_SNI
OpenSSL 库是否具有对 服务器名称指示 扩展的内置支持(如 RFC 6066 中所定义)。
3.2 版中的新功能。
- ssl.HAS_NPN
OpenSSL 库是否内置了对 下一个协议协商 的支持,如 应用层协议协商 中所述。 当为真时,您可以使用 SSLContext.set_npn_protocols() 方法来通告您想要支持的协议。
3.3 版中的新功能。
- ssl.HAS_SSLv2
OpenSSL 库是否内置了对 SSL 2.0 协议的支持。
3.7 版中的新功能。
- ssl.HAS_SSLv3
OpenSSL 库是否内置了对 SSL 3.0 协议的支持。
3.7 版中的新功能。
- ssl.HAS_TLSv1
OpenSSL 库是否内置了对 TLS 1.0 协议的支持。
3.7 版中的新功能。
- ssl.HAS_TLSv1_1
OpenSSL 库是否内置了对 TLS 1.1 协议的支持。
3.7 版中的新功能。
- ssl.HAS_TLSv1_2
OpenSSL 库是否内置了对 TLS 1.2 协议的支持。
3.7 版中的新功能。
- ssl.HAS_TLSv1_3
OpenSSL 库是否内置了对 TLS 1.3 协议的支持。
3.7 版中的新功能。
- ssl.CHANNEL_BINDING_TYPES
支持的 TLS 通道绑定类型列表。 此列表中的字符串可用作 SSLSocket.get_channel_binding() 的参数。
3.3 版中的新功能。
- ssl.OPENSSL_VERSION
解释器加载的 OpenSSL 库的版本字符串:
>>> ssl.OPENSSL_VERSION 'OpenSSL 1.0.2k 26 Jan 2017'
3.2 版中的新功能。
- ssl.OPENSSL_VERSION_INFO
一个由五个整数组成的元组,表示有关 OpenSSL 库的版本信息:
>>> ssl.OPENSSL_VERSION_INFO (1, 0, 2, 11, 15)
3.2 版中的新功能。
- ssl.OPENSSL_VERSION_NUMBER
OpenSSL 库的原始版本号,作为单个整数:
>>> ssl.OPENSSL_VERSION_NUMBER 268443839 >>> hex(ssl.OPENSSL_VERSION_NUMBER) '0x100020bf'
3.2 版中的新功能。
- ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILURE
ssl.ALERT_DESCRIPTION_INTERNAL_ERROR
ALERT_DESCRIPTION_* 来自 RFC 5246 等的警报描述。 IANA TLS Alert Registry 包含此列表和对定义其含义的 RFC 的引用。
在SSLContext.set_servername_callback()中用作回调函数的返回值。
3.4 版中的新功能。
- class ssl.AlertDescription
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。
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() 创建。 在早期版本中,可以直接创建实例。 这从未被记录或官方支持。
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()。
通常你应该使用像 recv() 和 send() 这样的套接字 API 方法而不是这些方法。
- SSLSocket.do_handshake()
执行 SSL 设置握手。
3.4 版本更改: 当socket 的context 的check_hostname 属性为真时,握手方法也会执行match_hostname()。
3.5 版更改: 每次接收或发送字节时,套接字超时不再重置。 套接字超时现在是握手的最大总持续时间。
在 3.7 版更改:主机名或 IP 地址在握手期间由 OpenSSL 匹配。 不再使用函数 match_hostname()。 如果 OpenSSL 拒绝主机名或 IP 地址,则会提前中止握手并向对等方发送 TLS 警报消息。
- SSLSocket.getpeercert(binary_form=False)
如果连接另一端的 peer 没有证书,则返回
None
。 如果 SSL 握手尚未完成,则引发 ValueError。如果
binary_form
参数为 False,并且从对等方收到证书,则此方法返回 dict 实例。 如果证书未经过验证,则 dict 为空。 如果证书被验证,它会返回一个带有几个键的字典,其中包括subject
(颁发证书的主体)和issuer
(颁发证书的主体)。 如果证书包含 主题备用名称 扩展的实例(请参阅 RFC 3280),则在字典。subject
和issuer
字段是包含证书数据结构中为各个字段提供的相对可分辨名称 (RDN) 序列的元组,每个 RDN 是名称-值对序列。 下面是一个真实世界的例子:{'issuer': ((('countryName', 'IL'),), (('organizationName', 'StartCom Ltd.'),), (('organizationalUnitName', 'Secure Digital Certificate Signing'),), (('commonName', 'StartCom Class 2 Primary Intermediate Server CA'),)), 'notAfter': 'Nov 22 08:15:19 2013 GMT', 'notBefore': 'Nov 21 03:09:52 2011 GMT', 'serialNumber': '95F0', 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),), (('countryName', 'US'),), (('stateOrProvinceName', 'California'),), (('localityName', 'San Francisco'),), (('organizationName', 'Electronic Frontier Foundation, Inc.'),), (('commonName', '*.eff.org'),), (('emailAddress', 'hostmaster@eff.org'),)), 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')), 'version': 3}
笔记
要验证特定服务的证书,您可以使用 match_hostname() 函数。
如果
binary_form
参数为 True,并且提供了证书,则此方法以字节序列形式返回整个证书的 DER 编码形式,或 None如果对等方没有提供证书。 对等方是否提供证书取决于 SSL 套接字的角色:对于客户端 SSL 套接字,服务器将始终提供证书,无论是否需要验证;
对于服务器 SSL 套接字,客户端只会在服务器请求时提供证书; 因此,如果您使用 CERT_NONE(而不是 CERT_OPTIONAL 或 CERT_24X],则 getpeercert() 将返回 None。
3.2 版本更改: 返回的字典包括额外的项目,例如
issuer
和notBefore
。在 3.4 版更改:ValueError 在未完成握手时引发。 返回的字典包括额外的 X509v3 扩展项,例如
crlDistributionPoints
、caIssuers
和OCSP
URI。在 3.8.1 版更改:IPv6 地址字符串不再有新行。
- SSLSocket.cipher()
- 返回一个三值元组,其中包含正在使用的密码的名称、定义其使用的 SSL 协议的版本以及正在使用的秘密位数。 如果没有建立连接,则返回
None
。
- SSLSocket.shared_ciphers()
返回握手期间客户端共享的密码列表。 返回列表的每个条目都是一个三值元组,其中包含密码的名称、定义其用途的 SSL 协议版本以及密码使用的秘密位数。 shared_ciphers() 如果没有建立连接或套接字是客户端套接字,则返回
None
。3.5 版中的新功能。
- SSLSocket.compression()
返回用作字符串的压缩算法,如果连接未压缩,则返回
None
。如果上层协议支持自己的压缩机制,可以使用 OP_NO_COMPRESSION 来禁用 SSL 级压缩。
3.3 版中的新功能。
- SSLSocket.get_channel_binding(cb_type='tls-unique')
获取当前连接的通道绑定数据,作为字节对象。 如果未连接或握手尚未完成,则返回
None
。cb_type 参数允许选择所需的通道绑定类型。 CHANNEL_BINDING_TYPES 列表中列出了有效的频道绑定类型。 目前仅支持由 RFC 5929 定义的“tls-unique”通道绑定。 ValueError 如果请求的通道绑定类型不受支持,则会引发。
3.3 版中的新功能。
- SSLSocket.selected_alpn_protocol()
返回在 TLS 握手期间选择的协议。 如果 SSLContext.set_alpn_protocols() 没有被调用,如果对方不支持 ALPN,如果这个套接字不支持任何客户端提出的协议,或者如果握手还没有发生,
None
返回。3.5 版中的新功能。
- SSLSocket.selected_npn_protocol()
返回在 TLS/SSL 握手期间选择的更高级别的协议。 如果 SSLContext.set_npn_protocols() 没有被调用,或者对方不支持 NPN,或者握手还没有发生,这将返回
None
。3.3 版中的新功能。
- SSLSocket.unwrap()
- 执行 SSL 关闭握手,从底层套接字中删除 TLS 层,并返回底层套接字对象。 这可用于将连接上的加密操作转换为未加密操作。 返回的套接字应始终用于与连接的另一端进行进一步通信,而不是原始套接字。
- SSLSocket.verify_client_post_handshake()
从 TLS 1.3 客户端请求握手后身份验证 (PHA)。 PHA 只能从服务器端套接字为 TLS 1.3 连接启动,在初始 TLS 握手之后并在双方启用 PHA,请参阅 SSLContext.post_handshake_auth。
该方法不会立即执行证书交换。 服务器端在下一次写入事件期间发送 CertificateRequest 并期望客户端在下一次读取事件中使用证书进行响应。
如果不满足任何先决条件(例如 不是 TLS 1.3,未启用 PHA),会引发 SSLError。
笔记
仅在启用 OpenSSL 1.1.1 和 TLS 1.3 时可用。 如果没有 TLS 1.3 支持,该方法会引发 NotImplementedError。
3.8 版中的新功能。
- SSLSocket.version()
以字符串形式返回连接协商的实际 SSL 协议版本,否则
None
未建立安全连接。 在撰写本文时,可能的返回值包括"SSLv2"
、"SSLv3"
、"TLSv1"
、"TLSv1.1"
和"TLSv1.2"
。 最近的 OpenSSL 版本可能会定义更多的返回值。3.5 版中的新功能。
- SSLSocket.pending()
- 返回可用于读取的已解密字节数,等待连接。
- SSLSocket.context
此 SSL 套接字绑定到的 SSLContext 对象。 如果 SSL 套接字是使用已弃用的 wrap_socket() 函数(而不是 SSLContext.wrap_socket())创建的,则这是为此 SSL 套接字创建的自定义上下文对象。
3.2 版中的新功能。
- SSLSocket.server_side
一个布尔值,服务器端套接字为
True
,客户端套接字为False
。3.2 版中的新功能。
- SSLSocket.server_hostname
服务器的主机名:str 类型,或
None
用于服务器端套接字,或者如果在构造函数中未指定主机名。3.2 版中的新功能。
3.7 版更改: 该属性现在始终为 ASCII 文本。 当
server_hostname
是国际化域名 (IDN) 时,此属性现在存储 A 标签形式("xn--pythn-mua.org"
),而不是 U 标签形式("pythön.org"
)。
- SSLSocket.session
此 SSL 连接的 SSLSession。 执行 TLS 握手后,会话可用于客户端和服务器端套接字。 对于客户端套接字,可以在调用 do_handshake() 以重用会话之前设置会话。
3.6 版中的新功能。
- SSLSocket.session_reused
3.6 版中的新功能。
SSL 上下文
3.2 版中的新功能。
SSL 上下文包含比单个 SSL 连接寿命更长的各种数据,例如 SSL 配置选项、证书和私钥。 它还管理服务器端套接字的 SSL 会话缓存,以加快来自相同客户端的重复连接。
- class ssl.SSLContext(protocol=PROTOCOL_TLS)
创建新的 SSL 上下文。 您可以传递 protocol,它必须是此模块中定义的
PROTOCOL_*
常量之一。 该参数指定要使用的 SSL 协议版本。 通常,服务器选择特定的协议版本,客户端必须适应服务器的选择。 大多数版本不能与其他版本互操作。 如果不指定,默认为PROTOCOL_TLS; 它提供了与其他版本的最大兼容性。这是一个表格,显示客户端中的哪些版本(侧面)可以连接到服务器中的哪些版本(顶部):
脚注
- 1(1,2)
SSLContext 默认使用 OP_NO_SSLv2 禁用 SSLv2。
- 2(1,2)
SSLContext 默认使用 OP_NO_SSLv3 禁用 SSLv3。
- 3(1,2)
TLS 1.3 协议将在 OpenSSL >= 1.1.1 中与 PROTOCOL_TLS 一起使用。 仅 TLS 1.3 没有专用的 PROTOCOL 常量。
也可以看看
create_default_context() 让 ssl 模块为给定目的选择安全设置。
在 3.6 版更改:使用安全默认值创建上下文。 选项 OP_NO_COMPRESSION、OP_CIPHER_SERVER_PREFERENCE、OP_SINGLE_DH_USE、OP_SINGLE_ECDH_USE[X127X1X18X1200X100X100X100X100X100X1000X100X1000X1000X1000X1000X1000X1000X5 ]) 和 OP_NO_SSLv3(PROTOCOL_SSLv3 除外)是默认设置的。 初始密码套件列表仅包含
HIGH
密码,没有NULL
密码和MD5
密码(PROTOCOL_SSLv2 除外)。
SSLContext 对象具有以下方法和属性:
- SSLContext.cert_store_stats()
获取有关加载的 X.509 证书数量、标记为 CA 证书的 X.509 证书计数以及作为字典的证书吊销列表的统计信息。
具有一个 CA 证书和另一个证书的上下文示例:
>>> context.cert_store_stats() {'crl': 0, 'x509_ca': 1, 'x509': 2}
3.4 版中的新功能。
- SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
加载私钥和相应的证书。 certfile 字符串必须是 PEM 格式的单个文件的路径,该文件包含证书以及建立证书真实性所需的任意数量的 CA 证书。 keyfile 字符串(如果存在)必须指向包含私钥的文件。 否则,私钥也将从 certfile 中获取。 有关证书如何存储在 certfile 中的更多信息,请参阅 Certificates 的讨论。
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)
当 verify_mode 不是 CERT_NONE 时,加载一组用于验证其他对等方证书的“证书颁发机构”(CA)证书。 必须至少指定 cafile 或 capath 之一。
此方法还可以加载 PEM 或 DER 格式的证书撤销列表 (CRL)。 为了使用 CRL,必须正确配置 SSLContext.verify_flags。
cafile 字符串(如果存在)是 PEM 格式的串联 CA 证书文件的路径。 有关如何在此文件中排列证书的更多信息,请参阅 Certificates 的讨论。
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() # OpenSSL 1.0.x [{'alg_bits': 256, 'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(256) Mac=AEAD', 'id': 50380848, 'name': 'ECDHE-RSA-AES256-GCM-SHA384', 'protocol': 'TLSv1/SSLv3', 'strength_bits': 256}, {'alg_bits': 128, 'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(128) Mac=AEAD', 'id': 50380847, 'name': 'ECDHE-RSA-AES128-GCM-SHA256', 'protocol': 'TLSv1/SSLv3', 'strength_bits': 128}]
在 OpenSSL 1.1 和更新版本上,密码字典包含附加字段:
>>> ctx.get_ciphers() # OpenSSL 1.1+ [{'aead': True, 'alg_bits': 256, 'auth': 'auth-rsa', 'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(256) Mac=AEAD', 'digest': None, 'id': 50380848, 'kea': 'kx-ecdhe', 'name': 'ECDHE-RSA-AES256-GCM-SHA384', 'protocol': 'TLSv1.2', 'strength_bits': 256, 'symmetric': 'aes-256-gcm'}, {'aead': True, 'alg_bits': 128, 'auth': 'auth-rsa', 'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA ' 'Enc=AESGCM(128) Mac=AEAD', 'digest': None, 'id': 50380847, 'kea': 'kx-ecdhe', 'name': 'ECDHE-RSA-AES128-GCM-SHA256', 'protocol': 'TLSv1.2', 'strength_bits': 128, 'symmetric': 'aes-128-gcm'}]
3.6 版中的新功能。
- SSLContext.set_default_verify_paths()
- 从构建 OpenSSL 库时定义的文件系统路径加载一组默认的“证书颁发机构”(CA)证书。 不幸的是,没有简单的方法可以知道此方法是否成功:如果找不到证书,则不会返回任何错误。 但是,当 OpenSSL 库作为操作系统的一部分提供时,它可能会被正确配置。
- SSLContext.set_ciphers(ciphers)
为使用此上下文创建的套接字设置可用密码。 它应该是 OpenSSL 密码列表格式 中的字符串。 如果无法选择密码(因为编译时选项或其他配置禁止使用所有指定的密码),则会引发 SSLError。
笔记
连接后,SSL 套接字的 SSLSocket.cipher() 方法将给出当前选择的密码。
OpenSSL 1.1.1 默认启用了 TLS 1.3 密码套件。 无法使用 set_ciphers() 禁用套件。
- SSLContext.set_alpn_protocols(protocols)
指定套接字在 SSL/TLS 握手期间应通告的协议。 它应该是一个 ASCII 字符串列表,如
['http/1.1', 'spdy/2']
,按偏好排序。 协议的选择将在握手期间进行,并将根据 RFC 7301 进行。 成功握手后, SSLSocket.selected_alpn_protocol() 方法将返回商定的协议。如果 HAS_ALPN 是
False
,此方法将引发 NotImplementedError。OpenSSL 1.1.0 到 1.1.0e 将中止握手并在双方都支持 ALPN 但无法就协议达成一致时引发 SSLError。 1.1.0f+ 的行为类似于 1.0.2,SSLSocket.selected_alpn_protocol() 返回 None。
3.5 版中的新功能。
- SSLContext.set_npn_protocols(protocols)
指定套接字在 SSL/TLS 握手期间应通告的协议。 它应该是一个字符串列表,如
['http/1.1', 'spdy/2']
,按偏好排序。 协议的选择将在握手期间进行,并将根据 应用层协议协商 进行。 成功握手后, SSLSocket.selected_npn_protocol() 方法将返回商定的协议。如果 HAS_NPN 是
False
,此方法将引发 NotImplementedError。3.3 版中的新功能。
- SSLContext.sni_callback
注册一个回调函数,当 TLS 客户端指定服务器名称指示时,SSL/TLS 服务器收到 TLS 客户端 Hello 握手消息后将调用该回调函数。 服务器名称指示机制在 RFC 6066 第 3 节 - 服务器名称指示中指定。
每个
SSLContext
只能设置一个回调。 如果 sni_callback 设置为None
,则回调被禁用。 后续调用此函数将禁用先前注册的回调。将使用三个参数调用回调函数; 第一个是 ssl.SSLSocket,第二个是表示客户端打算通信的服务器名称的字符串(如果 TLS Client Hello 不包含服务器,则为 None name),第三个参数是原始的 SSLContext。 服务器名称参数是文本。 对于国际化域名,服务器名称是 IDN A 标签 (
"xn--pythn-mua.org"
)。此回调的典型用途是将 ssl.SSLSocket 的 SSLSocket.context 属性更改为 SSLContext 类型的新对象,表示匹配的证书链服务器名称。
由于 TLS 连接的早期协商阶段,只有有限的方法和属性可用,如 SSLSocket.selected_alpn_protocol() 和 SSLSocket.context。 SSLSocket.getpeercert(), SSLSocket.getpeercert(), SSLSocket.cipher() 和
SSLSocket.compress()
方法要求 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 版中的新功能。
- SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=None, session=None)
包装现有的 Python 套接字 sock 并返回 SSLContext.sslsocket_class 的实例(默认 SSLSocket)。 返回的 SSL 套接字与上下文、其设置和证书相关联。 sock 必须是 SOCK_STREAM 套接字; 不支持其他套接字类型。
参数
server_side
是一个布尔值,用于标识此套接字是否需要服务器端或客户端行为。对于客户端套接字,上下文构造是惰性的; 如果底层套接字尚未连接,则在套接字上调用
connect()
后将执行上下文构造。 对于服务器端套接字,如果该套接字没有远程对等方,则假定它是一个侦听套接字,并且服务器端 SSL 包装会在通过accept()
方法接受的客户端连接上自动执行。 该方法可能会引发 SSLError。在客户端连接上,可选参数 server_hostname 指定我们正在连接的服务的主机名。 这允许单个服务器使用不同的证书托管多个基于 SSL 的服务,这与 HTTP 虚拟主机非常相似。 如果 server_side 为真,指定 server_hostname 将引发 ValueError。
参数
do_handshake_on_connect
指定在做了socket.connect()
后是否自动进行SSL握手,或者应用程序是否会通过调用SSLSocket.do_handshake()显式调用它方法。 显式调用 SSLSocket.do_handshake() 使程序可以控制握手中涉及的套接字 I/O 的阻塞行为。参数
suppress_ragged_eofs
指定SSLSocket.recv()
方法应如何从连接的另一端发出意外 EOF 信号。 如果指定为 True(默认值),它将返回一个正常的 EOF(一个空字节对象)以响应从底层套接字引发的意外 EOF 错误; 如果 False,它会将异常返回给调用者。会话,见会话。
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 传递给 Xwrap_socket3(X146X]Xwrap_socket3(X76X)为了匹配主机名。 启用主机名检查会自动将 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_REQUIRED 并且 verify_mode 是 X1ONEX] ]。 以前,相同的操作会因 ValueError 而失败。
笔记
此功能需要 OpenSSL 0.9.8f 或更新版本。
- SSLContext.keylog_filename
每当生成或接收密钥材料时,将 TLS 密钥写入密钥日志文件。 键盘日志文件仅用于调试目的。 文件格式由 NSS 指定,并被许多流量分析器(例如 Wireshark)使用。 日志文件以仅附加模式打开。 写入在线程之间同步,但在进程之间不同步。
3.8 版中的新功能。
笔记
此功能需要 OpenSSL 1.1.1 或更新版本。
- SSLContext.maximum_version
表示支持的最高 TLS 版本的 TLSVersion 枚举成员。 该值默认为 TLSVersion.MAXIMUM_SUPPORTED。 对于除 PROTOCOL_TLS、PROTOCOL_TLS_CLIENT 和 PROTOCOL_TLS_SERVER 之外的协议,该属性是只读的。
属性 maximum_version、minimum_version 和 SSLContext.options 都会影响上下文支持的 SSL 和 TLS 版本。 该实现不会阻止无效组合。 例如,options 和 maximum_version 中的 OP_NO_TLSv1_2 设置为 TLSVersion.TLSv1_2 的上下文将无法建立 TLS 1.2 连接。
笔记
除非使用 OpenSSL 1.1.0g 或更新版本编译 ssl 模块,否则此属性不可用。
3.7 版中的新功能。
- SSLContext.minimum_version
与 SSLContext.maximum_version 类似,但它是支持的最低版本或 TLSVersion.MINIMUM_SUPPORTED。
笔记
除非使用 OpenSSL 1.1.0g 或更新版本编译 ssl 模块,否则此属性不可用。
3.7 版中的新功能。
- SSLContext.num_tickets
控制
TLS_PROTOCOL_SERVER
上下文的 TLS 1.3 会话票证数量。 该设置对 TLS 1.0 到 1.2 连接没有影响。笔记
除非使用 OpenSSL 1.1.1 或更新版本编译 ssl 模块,否则此属性不可用。
3.8 版中的新功能。
- SSLContext.options
一个整数,表示在此上下文中启用的 SSL 选项集。 默认值为 OP_ALL,但您可以通过将它们组合在一起来指定其他选项,例如 OP_NO_SSLv2。
笔记
对于低于 0.9.8m 的 OpenSSL 版本,只能设置选项,不能清除它们。 尝试清除选项(通过重置相应位)将引发 ValueError。
3.6 版更改:SSLContext.options 返回 Options 标志:
>>> ssl.create_default_context().options <Options.OP_ALL|OP_NO_SSLv3|OP_NO_SSLv2|OP_NO_COMPRESSION: 2197947391>
- SSLContext.post_handshake_auth
启用 TLS 1.3 握手后客户端身份验证。 默认情况下禁用握手后身份验证,服务器只能在初始握手期间请求 TLS 客户端证书。 启用后,服务器可以在握手后随时请求 TLS 客户端证书。
在客户端套接字上启用时,客户端会向服务器发出信号,表明它支持握手后身份验证。
在服务器端套接字上启用时,SSLContext.verify_mode 也必须设置为 CERT_OPTIONAL 或 CERT_REQUIRED。 实际的客户端证书交换被延迟,直到 SSLSocket.verify_client_post_handshake() 被调用并执行一些 I/O。
笔记
仅在启用 OpenSSL 1.1.1 和 TLS 1.3 时可用。 没有TLS 1.3支持,属性值为None,不可修改
3.8 版中的新功能。
- SSLContext.protocol
- 构建上下文时选择的协议版本。 该属性是只读的。
- SSLContext.hostname_checks_common_name
check_hostname 在没有主题替代名称扩展的情况下是否回退以验证证书的主题通用名称(默认值:true)。
笔记
只能使用 OpenSSL 1.1.0 或更高版本写入。
3.7 版中的新功能。
3.9.3 版更改: 该标志在 1.1.1k 版之前对 OpenSSL 无效。 Python 3.8.9、3.9.3 和 3.10 包括以前版本的变通方法。
- SSLContext.verify_flags
证书验证操作的标志。 您可以通过将它们组合在一起来设置像 VERIFY_CRL_CHECK_LEAF 这样的标志。 默认情况下,OpenSSL 既不需要也不需要验证证书吊销列表 (CRL)。 仅适用于 openssl 版本 0.9.8+。
3.4 版中的新功能。
3.6 版更改: SSLContext.verify_flags 返回 VerifyFlags 标志:
>>> ssl.create_default_context().verify_flags <VerifyFlags.VERIFY_X509_TRUSTED_FIRST: 32768>
- SSLContext.verify_mode
是否尝试验证其他对等方的证书以及验证失败时的行为。 此属性必须是 CERT_NONE、CERT_OPTIONAL 或 CERT_REQUIRED 之一。
3.6 版更改: SSLContext.verify_mode 返回 VerifyMode 枚举:
>>> ssl.create_default_context().verify_mode <VerifyMode.CERT_REQUIRED: 2>
证书
证书通常是公钥/私钥系统的一部分。 在这个系统中,每个principal(可能是一台机器、一个人或一个组织)都被分配了一个唯一的两部分加密密钥。 密钥的一部分是公开的,称为公钥; 另一部分保密,称为私钥。 这两个部分是相关的,因为如果您用其中一个部分加密消息,您可以用另一部分解密,而 仅 与另一部分。
一个证书包含有关两个主体的信息。 它包含 主题 的名称和主题的公钥。 它还包含第二个委托人 发行人 的声明,该主体是他们声称的身份,并且这确实是该主体的公钥。 发行人的声明是用发行人的私钥签名的,只有发行人知道。 但是,任何人都可以通过找到发行者的公钥、用它解密声明并将其与证书中的其他信息进行比较来验证发行者的声明。 该证书还包含有关其有效时间段的信息。 这表示为两个字段,称为“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() 和 wrap_socket() 的 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.mydomain.com', 10023))
bindsocket.listen(5)
当客户端连接时,您将在套接字上调用 accept()
以从另一端获取新的套接字,并使用上下文的 SSLContext.wrap_socket() 方法创建服务器端用于连接的 SSL 套接字:
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
然后您将从 connstream
读取数据并对其进行处理,直到您完成对客户端的处理(或客户端对您完成处理):
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
if not do_something(connstream, data):
# we'll assume do_something returns False
# when we're finished with client
break
data = connstream.recv(1024)
# finished with client
然后返回侦听新的客户端连接(当然,真正的服务器可能会在单独的线程中处理每个客户端连接,或者将套接字置于 非阻塞模式 并使用事件循环)。
关于非阻塞套接字的注意事项
SSL 套接字的行为与非阻塞模式下的常规套接字略有不同。 使用非阻塞套接字时,您需要注意以下几点:
如果 I/O 操作会阻塞,大多数 SSLSocket 方法将引发 SSLWantWriteError 或 SSLWantReadError 而不是 BlockingIOError。 SSLWantReadError 如果需要对底层套接字进行读操作,将引发 SSLWantWriteError 对底层套接字进行写操作。 请注意,尝试向 SSL 套接字 写入 可能需要首先从底层套接字 读取 ,而尝试从 SSL 套接字 读取 可能需要事先 ]write 到底层套接字。
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 模块支持 [X37X] 非阻塞 SSL 套接字 并提供更高级别的 API。 它使用 selectors 模块轮询事件并处理 SSLWantWriteError、SSLWantReadError 和 BlockingIOError 异常。 它也异步运行 SSL 握手。
内存生物支持
3.5 版中的新功能。
自从 Python 2.6 中引入 SSL 模块以来,SSLSocket 类提供了两个相关但不同的功能领域:
- SSL 协议处理
- 网络IO
网络 IO API 与 socket.socket 提供的相同,SSLSocket 也从中继承。 这允许使用 SSL 套接字作为常规套接字的直接替代品,从而非常容易地向现有应用程序添加 SSL 支持。
将 SSL 协议处理和网络 IO 结合使用通常效果很好,但在某些情况下则不然。 一个例子是异步 IO 框架想要使用不同的 IO 多路复用模型,而不是由 socket.socket 和内部 OpenSSL 套接字假定的“文件描述符上的选择/轮询”(基于就绪性)模型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。 incoming BIO 用于将数据从 Python 传递到 SSL 协议实例,而 outgoing BIO 用于以相反的方式传递数据。
可以使用以下方法:
与 SSLSocket 相比,此对象缺少以下功能:
任何形式的网络IO;
recv()
和send()
仅读写底层 MemoryBIO 缓冲区。没有 do_handshake_on_connect 机器。 您必须始终手动调用 do_handshake() 以开始握手。
没有处理 suppress_ragged_eofs。 所有违反协议的文件结束条件都通过 SSLEOFError 异常报告。
方法 unwrap() 调用不返回任何内容,这与它返回底层套接字的 SSL 套接字不同。
传递给 SSLContext.set_servername_callback() 的 server_name_callback 回调将获得 SSLObject 实例而不是 SSLSocket 实例作为其第一个参数。
使用 SSLObject 的一些注意事项:
SSLObject 上的所有 IO 都是 非阻塞 。 这意味着,例如 read() 将引发 SSLWantReadError 如果它需要比传入 BIO 可用的更多数据。
没有像 wrap_socket() 那样的模块级
wrap_bio()
调用。 SSLObject 总是通过 SSLContext 创建。
3.7 版更改:SSLObject 实例必须使用 wrap_bio() 创建。 在早期版本中,可以直接创建实例。 这从未被记录或官方支持。
SSLObject 使用内存缓冲区与外部世界进行通信。 类 MemoryBIO 提供了可用于此目的的内存缓冲区。 它包装了一个 OpenSSL 内存 BIO(基本 IO)对象:
- class ssl.MemoryBIO
可用于在 Python 和 SSL 协议实例之间传递数据的内存缓冲区。
- pending
返回当前内存缓冲区中的字节数。
- eof
一个布尔值,指示内存 BIO 是否在文件末尾位置是当前的。
- read(n=- 1)
从内存缓冲区中读取最多 n 个字节。 如果 n 未指定或为负,则返回所有字节。
- write(buf)
将 buf 中的字节写入内存 BIO。 buf 参数必须是支持缓冲协议的对象。
返回值是写入的字节数,始终等于buf的长度。
SSL 会话
3.6 版中的新功能。
- class ssl.SSLSession
session 使用的会话对象。
- id
- time
- timeout
- ticket_lifetime_hint
- has_ticket
安全考虑
最佳默认值
对于客户端使用,如果您对安全策略没有任何特殊要求,强烈建议您使用create_default_context()函数来创建您的SSL上下文。 它将加载系统的可信 CA 证书,启用证书验证和主机名检查,并尝试选择合理安全的协议和密码设置。
例如,以下是您将如何使用 smtplib.SMTP 类创建到 SMTP 服务器的可信、安全连接:
>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')
如果连接需要客户端证书,可以添加 SSLContext.load_cert_chain()。
相比之下,如果您通过自己调用 SSLContext 构造函数来创建 SSL 上下文,则默认情况下不会启用证书验证或主机名检查。 如果您这样做,请阅读以下段落以达到良好的安全级别。
手动设置
验证证书
直接调用 SSLContext 构造函数时,CERT_NONE 是默认值。 由于它不对其他对等点进行身份验证,因此它可能不安全,尤其是在客户端模式下,大多数时候您都希望确保与之交谈的服务器的真实性。 因此,在客户端模式下,强烈建议使用 CERT_REQUIRED。 然而,这本身是不够的; 您还必须检查可以通过调用 SSLSocket.getpeercert() 获得的服务器证书是否与所需的服务匹配。 对于许多协议和应用程序,可以通过主机名来标识服务; 在这种情况下,可以使用 match_hostname() 函数。 当启用 SSLContext.check_hostname 时,会自动执行此常见检查。
3.7 版更改: 主机名匹配现在由 OpenSSL 执行。 Python 不再使用 match_hostname()。
在服务器模式下,如果您想使用 SSL 层(而不是使用更高级别的身份验证机制)对您的客户端进行身份验证,您还必须指定 CERT_REQUIRED 并类似地检查客户端证书。
协议版本
SSL 版本 2 和 3 被认为是不安全的,因此使用起来很危险。 如果你想要客户端和服务器之间的最大兼容性,建议使用 PROTOCOL_TLS_CLIENT 或 PROTOCOL_TLS_SERVER 作为协议版本。 默认情况下禁用 SSLv2 和 SSLv3。
>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.options |= ssl.OP_NO_TLSv1
>>> client_context.options |= ssl.OP_NO_TLSv1_1
上面创建的 SSL 上下文将只允许 TLSv1.2 和更高版本(如果您的系统支持)连接到服务器。 PROTOCOL_TLS_CLIENT 默认意味着证书验证和主机名检查。 您必须将证书加载到上下文中。
密码选择
如果您有高级安全要求,则可以通过 SSLContext.set_ciphers() 方法微调协商 SSL 会话时启用的密码。 从 Python 3.2.3 开始,ssl 模块默认禁用某些弱密码,但您可能希望进一步限制密码选择。 请务必阅读有关 密码列表格式 的 OpenSSL 文档。 如果要检查给定密码列表启用了哪些密码,请在系统上使用 SSLContext.get_ciphers() 或 openssl ciphers
命令。
多处理
如果将此模块用作多处理应用程序的一部分(例如使用 multiprocessing 或 concurrent.futures 模块),请注意 OpenSSL 的内部随机数生成器无法正确处理分叉进程。 如果应用程序通过 os.fork() 使用任何 SSL 功能,则应用程序必须更改父进程的 PRNG 状态。 任何成功调用 RAND_add()、RAND_bytes() 或 RAND_pseudo_bytes() 就足够了。
TLS 1.3
3.7 版中的新功能。
Python 对带有 OpenSSL 1.1.1 的 TLS 1.3 提供临时和实验性支持。 新协议的行为与以前版本的 TLS/SSL 略有不同。 一些新的 TLS 1.3 功能尚不可用。
- TLS 1.3 使用一组分离的密码套件。 默认情况下启用所有 AES-GCM 和 ChaCha20 密码套件。 方法 SSLContext.set_ciphers() 还不能启用或禁用任何 TLS 1.3 密码,但 SSLContext.get_ciphers() 会返回它们。
- 会话票不再作为初始握手的一部分发送,并且处理方式不同。 SSLSocket.session 和 SSLSession 与 TLS 1.3 不兼容。
- 在初始握手期间也不再验证客户端证书。 服务器可以随时请求证书。 客户端在从服务器发送或接收应用程序数据时处理证书请求。
- 尚不支持 TLS 1.3 功能,如早期数据、延迟 TLS 客户端证书请求、签名算法配置和重新生成密钥。
LibreSSL 支持
LibreSSL 是 OpenSSL 1.0.1 的一个分支。 ssl 模块对 LibreSSL 的支持有限。 使用 LibreSSL 编译 ssl 模块时,某些功能不可用。
- LibreSSL >= 2.6.1 不再支持 NPN。 方法 SSLContext.set_npn_protocols() 和 SSLSocket.selected_npn_protocol() 不可用。
- SSLContext.set_default_verify_paths() 忽略环境变量
SSL_CERT_FILE
和SSL_CERT_PATH
尽管 _get_paths9_verifyX]仍然报告他们。
也可以看看
- 类 socket.socket
底层 socket 类的文档
- SSL/TLS 强加密:简介
来自 Apache HTTP Server 文档的介绍
- RFC 1422:Internet 电子邮件的隐私增强:第二部分:基于证书的密钥管理
史蒂夫·肯特
- RFC 4086:安全的随机性要求
唐纳德 E.,杰弗里 I. 席勒
- RFC 5280:Internet X.509 公钥基础设施证书和证书撤销列表 (CRL) 配置文件
库柏
- RFC 5246:传输层安全 (TLS) 协议 1.2 版
迪尔克斯等。 阿尔。
- RFC 6066:传输层安全 (TLS) 扩展
东湖
- IANA TLS:传输层安全 (TLS) 参数
IANA
- RFC 7525:安全使用传输层安全性 (TLS) 和数据报传输层安全性 (DTLS) 的建议
国际工作组
- Mozilla 的服务器端 TLS 建议
Mozilla