Skip to main content

18.2. ssl —套接字对象的TLS/SSL包装器

源代码: Lib/ssl.py


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

注解

某些行为可能依赖于平台,因为调用操作系统套接字API。安装的OpenSSL版本也可能导致行为的变化。例如,TLSv1.1和TLSv1.2附带openssl版本1.0.1。

警告

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

本节介绍 ssl 模块中的对象和功能;有关TLS,SSL和证书的更多一般信息,请参阅底部“另请参阅”部分中的文档。

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

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

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

18.2.1. 函数,常量和异常

exception ssl.SSLError

引发来自底层SSL实现(当前由OpenSSL库提供)的错误信号。这表示在基础网络连接上叠加的较高级别加密和认证层中的一些问题。此错误是 OSError 的子类型。 SSLError 实例的错误代码和消息由OpenSSL库提供。

在 3.3 版更改: SSLError 曾经是 socket.error 的亚型。

library

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

3.3 新版功能.

reason

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

3.3 新版功能.

exception ssl.SSLZeroReturnError

尝试读取或写入时,SSLError 的子类提出,并且SSL连接已完全关闭。请注意,这并不意味着底层传输(读TCP)已经关闭。

3.3 新版功能.

exception ssl.SSLWantReadError

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

3.3 新版功能.

exception ssl.SSLWantWriteError

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

3.3 新版功能.

exception ssl.SSLSyscallError

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

3.3 新版功能.

exception ssl.SSLEOFError

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

3.3 新版功能.

exception ssl.CertificateError

被提示用信号通知证书错误(例如主机名不匹配)。但是,OpenSSL检测到的证书错误引发了 SSLError

18.2.1.1. 套接字创建

以下函数允许创建独立套接字。从Python 3.2开始,它可以更灵活地使用 SSLContext.wrap_socket()

ssl.wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, 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 套接字;不支持其他套接字类型。

对于客户端套接字,上下文构造是惰性的;如果底层套接字尚未连接,则将在套接字上调用 connect() 之后执行上下文构造。对于服务器端套接字,如果套接字没有远程对等端,则假定它是侦听套接字,并且服务器端SSL包装在通过 accept() 方法接受的客户端连接上自动执行。 wrap_socket() 可能引起 SSLError

keyfilecertfile 参数指定包含要用于标识连接的本地端的证书的可选文件。有关如何将证书存储在 certfile 中的更多信息,请参阅 证书 的讨论。

参数 server_side 是一个布尔值,用于标识该套接字是否需要服务器端或客户端行为。

参数 cert_reqs 指定是否需要来自连接的另一侧的证书,以及是否将被验证(如果提供的话)。它必须是三个值之一:CERT_NONE (忽略证书),CERT_OPTIONAL (不是必需的,但如果提供,则验证)或 CERT_REQUIRED (必需和验证)。如果此参数的值不是 CERT_NONE,则 ca_certs 参数必须指向CA证书的文件。

ca_certs 文件包含一组级联的“证书颁发机构”证书,用于验证从连接的另一端传递的证书。有关如何在此文件中排列证书的更多信息,请参阅 证书 的讨论。

参数 ssl_version 指定要使用的SSL协议的哪个版本。通常,服务器选择特定的协议版本,并且客户端必须适应服务器的选择。大多数版本不能与其他版本互操作。如果没有指定,默认为 PROTOCOL_TLS;它提供了与其他版本最大的兼容性。

这里有一个表,显示客户端(下方)中的哪些版本可以连接到服务器中的哪些版本(沿顶部):

client / 服务器

SSLv2

SSLv3

TLS

TLSv1

TLSv1.1

TLSv1.2

SSLv2

没有

[1]

没有

没有

没有

SSLv3

没有

[2]

没有

没有

没有

TLSSSLv23

[1]

[2]

TLSv1

没有

没有

没有

没有

TLSv1.1

没有

没有

没有

没有

TLSv1.2

没有

没有

没有

没有

脚注

[1](1, 2)

默认情况下,SSLContext 会使用 OP_NO_SSLv2 禁用SSLv2。

[2](1, 2)

默认情况下,SSLContext 将使用 OP_NO_SSLv3 禁用SSLv3。

注解

哪些连接成功将取决于OpenSSL的版本。例如,在OpenSSL 1.0.0之前,SSLv23客户端将始终尝试SSLv2连接。

ciphers 参数设置此SSL对象的可用密码。它应该是 OpenSSL加密列表格式 中的字符串。

参数 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.2 版更改: 新的可选参数 ciphers

18.2.1.2. 上下文创建

方便功能有助于为常见目的创建 SSLContext 对象。

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

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

cafilecapathcadata 表示可选的CA证书信任的证书验证,如在 SSLContext.load_verify_locations()。如果所有三个都是 None,此功能可以选择信任系统的默认CA证书。

设置为:PROTOCOL_TLSOP_NO_SSLv2OP_NO_SSLv3,具有高加密密码套件,不带RC4和未经验证的密码套件。传递 SERVER_AUTH 作为 purposeverify_mode 设置为 CERT_REQUIRED 并且加载CA证书(当给出 cafilecapathcadata 中的至少一个时)或使用 SSLContext.load_default_certs() 加载默认CA证书。

注解

协议,选项,密码和其他设置可以随时更改为更具限制性的值,而无需先前的淘汰。这些值表示兼容性和安全性之间的公平平衡。

如果应用程序需要特定设置,则应创建 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从默认密码字符串中删除。

18.2.1.3. 随机生成

ssl.RAND_bytes(num)

返回 num 加密强伪随机字节。如果PRNG没有播种足够的数据或如果当前RAND方法不支持操作,则提高 SSLErrorRAND_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/ 了解熵收集守护程序的来源。

可用性:不适用于LibreSSL和OpenSSL> 1.1.0

ssl.RAND_add(bytes, entropy)

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

在 3.5 版更改: 可写 bytes-like object 现已被接受。

18.2.1.4. 证书处理

ssl.match_hostname(cert, hostname)

验证 cert (以 SSLSocket.getpeercert() 返回的解码格式)与给定的 hostname 匹配。所应用的规则是用于检查 RFC 2818RFC 6125 中概述的HTTPS服务器的身份的规则。除了HTTPS之外,此功能应适用于检查各种基于SSL的协议(如FTPS,IMAPS,POPS等)中的服务器的身份。

CertificateError 在故障时升高。成功时,函数不返回任何内容:

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

3.2 新版功能.

在 3.3.3 版更改: 该功能现在遵循 RFC 6125,第6.4.3节,既不匹配多个通配符(例如 *.*.com*a*.example.org),也不匹配国际化域名(IDN)片段中的通配符。仍然支持IDN A标签(例如 www*.xn--pthon-kva.org),但 x*.python.org 不再匹配 xn--tda.python.org

在 3.5 版更改: 现在支持在IP证书的subjectAltName字段中存在IP地址的匹配。

ssl.cert_time_to_seconds(cert_time)

"%b %d %H:%M:%S %Y %Z" strptime格式(C语言环境)中的证书为基础,给出表示“notBefore”或“notAfter”日期的 cert_time 字符串,返回自纪元以来的时间(以秒为单位)。

这里有一个例子:

>>> 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 作为(hostnameport-number)对,提取服务器的证书,并将其作为PEM编码的字符串返回。如果指定了 ssl_version,则使用该版本的SSL协议尝试连接到服务器。如果指定 ca_certs,它应该是一个包含根证书列表的文件,其格式与 wrap_socket() 中相同参数使用的格式相同。该调用将尝试根据该组根证书验证服务器证书,并且如果验证尝试失败,则失败。

在 3.3 版更改: 此功能现在兼容IPv6。

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

ssl.DER_cert_to_PEM_cert(DER_cert_bytes)

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

ssl.PEM_cert_to_DER_cert(PEM_cert_string)

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

ssl.get_default_verify_paths()

返回具有OpenSSL的默认cafile和capath的路径的命名元组。路径与 SSLContext.set_default_verify_paths() 使用的路径相同。返回值是 named tuple DefaultVerifyPaths

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

  • capath - 解析路径到capath或 None,如果目录不存在,

  • openssl_cafile_env - OpenSSL环境键,指向一个cafile,

  • openssl_cafile - 硬编码路径到cafile,

  • openssl_capath_env - OpenSSL的环境键,指向一个capath,

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

可用性:LibreSSL忽略环境vars openssl_cafile_envopenssl_capath_env

3.4 新版功能.

ssl.enum_certificates(store_name)

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

该函数返回一个(cert_bytes,encoding_type,trust)元组的列表。 encoding_type指定cert_bytes的编码。它是用于X.509 ASN.1数据的 x509_asn 或用于PKCS#7 ASN.1数据的 pkcs_7_asn。信任将证书的目的指定为一组OIDS或完全 True,如果证书对于所有目的都是可信赖的。

例:

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

可用性:Windows。

3.4 新版功能.

ssl.enum_crls(store_name)

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

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

可用性:Windows。

3.4 新版功能.

18.2.1.5. 常量

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

3.6 新版功能.

ssl.CERT_NONE

SSLContext.verify_mode 的可能值,或 wrap_socket()cert_reqs 参数。在此模式下(默认),将不需要来自套接字连接另一端的证书。如果从另一端接收到证书,则不尝试验证它。

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

ssl.CERT_OPTIONAL

SSLContext.verify_mode 的可能值,或 wrap_socket()cert_reqs 参数。在此模式下,将不需要来自套接字连接的另一侧的证书;但如果提供,将尝试进行验证,并在失败时提出 SSLError

使用此设置需要传递一组有效的CA证书到 SSLContext.load_verify_locations() 或作为 wrap_socket()ca_certs 参数的值。

ssl.CERT_REQUIRED

SSLContext.verify_mode 的可能值,或 wrap_socket()cert_reqs 参数。在此模式下,需要从套接字连接的另一端获取证书;如果没有提供证书,或者如果其验证失败,则会提出 SSLError

使用此设置需要传递一组有效的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,验证将失败。

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

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

3.6 新版功能.

ssl.PROTOCOL_TLS_CLIENT

自动协商最高协议版本,如 PROTOCOL_SSLv23,但只支持客户端 SSLSocket 连接。默认情况下,协议启用 CERT_REQUIREDcheck_hostname

3.6 新版功能.

ssl.PROTOCOL_TLS_SERVER

自动协商最高协议版本(如 PROTOCOL_SSLv23),但仅支持服务器端 SSLSocket 连接。

3.6 新版功能.

ssl.PROTOCOL_SSLv23

数据别名:PROTOCOL_TLS

3.6 版后已移除: 使用数据:PROTOCOL_TLS

ssl.PROTOCOL_SSLv2

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

如果OpenSSL使用 OPENSSL_NO_SSL2 标志编译,则此协议不可用。

警告

SSL版本2不安全。它的使用是高度不鼓励。

3.6 版后已移除: OpenSSL已取消对SSLv2的支持。

ssl.PROTOCOL_SSLv3

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

如果OpenSSL使用 OPENSSL_NO_SSLv3 标志编译,则此协议不可用。

警告

SSL版本3不安全。它的使用是高度不鼓励。

3.6 版后已移除: OpenSSL已弃用所有特定于版本的协议。使用默认协议数据:PROTOCOL_TLS 替换为数据:OP_NO_SSLv3

ssl.PROTOCOL_TLSv1

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

3.6 版后已移除: OpenSSL已弃用所有特定于版本的协议。使用默认协议数据:PROTOCOL_TLS 替换为数据:OP_NO_SSLv3

ssl.PROTOCOL_TLSv1_1

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

3.4 新版功能.

3.6 版后已移除: OpenSSL已弃用所有特定于版本的协议。使用默认协议数据:PROTOCOL_TLS 替换为数据:OP_NO_SSLv3

ssl.PROTOCOL_TLSv1_2

选择TLS版本1.2作为信道加密协议。这是最现代的版本,可能是最大的保护,如果双方可以说话的最佳选择。仅适用于openssl版本1.0.1+。

3.4 新版功能.

3.6 版后已移除: OpenSSL已弃用所有特定于版本的协议。使用默认协议数据:PROTOCOL_TLS 替换为数据:OP_NO_SSLv3

ssl.OP_ALL

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

3.2 新版功能.

ssl.OP_NO_SSLv2

阻止SSLv2连接。此选项仅适用于 PROTOCOL_TLS。它防止对等体选择SSLv2作为协议版本。

3.2 新版功能.

3.6 版后已移除: SSLv2已弃用

ssl.OP_NO_SSLv3

阻止SSLv3连接。此选项仅适用于 PROTOCOL_TLS。它防止对等体选择SSLv3作为协议版本。

3.2 新版功能.

3.6 版后已移除: SSLv3已弃用

ssl.OP_NO_TLSv1

阻止TLSv1连接。此选项仅适用于 PROTOCOL_TLS。它防止对等体选择TLSv1作为协议版本。

3.2 新版功能.

ssl.OP_NO_TLSv1_1

阻止TLSv1.1连接。此选项仅适用于 PROTOCOL_TLS。它防止对等体选择TLSv1.1作为协议版本。仅适用于openssl版本1.0.1+。

3.4 新版功能.

ssl.OP_NO_TLSv1_2

阻止TLSv1.2连接。此选项仅适用于 PROTOCOL_TLS。它防止对等体选择TLSv1.2作为协议版本。仅适用于openssl版本1.0.1+。

3.4 新版功能.

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_NO_COMPRESSION

禁用SSL通道上的压缩。如果应用协议支持其自己的压缩方案,这是有用的。

此选项仅适用于OpenSSL 1.0.0和更高版本。

3.3 新版功能.

class ssl.Options

enum.IntFlag 的 OP_* 常量集合。

ssl.OP_NO_TICKET

阻止客户端请求会话票证。

3.6 新版功能.

ssl.HAS_ALPN

OpenSSL库是否具有对 RFC 7301 中描述的 应用层协议协商 TLS扩展的内置支持。

3.5 新版功能.

ssl.HAS_ECDH

OpenSSL库是否支持基于椭圆曲线的Diffie-Hellman密钥交换。这应该是真的,除非分布器明确禁用了该功能。

3.3 新版功能.

ssl.HAS_SNI

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

3.2 新版功能.

ssl.HAS_NPN

OpenSSL库是否具有对 NPN草案规范 中描述的 下一协议谈判 的内置支持。如果为true,您可以使用 SSLContext.set_npn_protocols() 方法来宣传您要支持的协议。

3.3 新版功能.

ssl.CHANNEL_BINDING_TYPES

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

3.3 新版功能.

ssl.OPENSSL_VERSION

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

>>> ssl.OPENSSL_VERSION
'OpenSSL 0.9.8k 25 Mar 2009'

3.2 新版功能.

ssl.OPENSSL_VERSION_INFO

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

>>> ssl.OPENSSL_VERSION_INFO
(0, 9, 8, 11, 15)

3.2 新版功能.

ssl.OPENSSL_VERSION_NUMBER

OpenSSL库的原始版本号,作为单个整数:

>>> ssl.OPENSSL_VERSION_NUMBER
9470143
>>> hex(ssl.OPENSSL_VERSION_NUMBER)
'0x9080bf'

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

18.2.2. SSL套接字

class ssl.SSLSocket(socket.socket)

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

然而,由于SSL(和TLS)协议在TCP的顶部具有自己的成帧,所以SSL套接字抽象在某些方面可能偏离正常的OS级套接字的规范。特别参见 非阻塞套接字的注释

通常,SSLSocket 不是直接创建的,而是使用 SSLContext.wrap_socket() 方法。

在 3.5 版更改: 添加 sendfile() 方法。

在 3.5 版更改: 每次接收或发送字节时,shutdown() 不会重置套接字超时。套接字超时现在是关机的总持续时间的最大值。

3.6 版后已移除: 不推荐直接创建 SSLSocket 实例,使用 SSLContext.wrap_socket() 包装套接字。

SSL套接字还具有以下其他方法和属性:

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

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

如果套接字是 非阻塞,读取将阻塞,则升高 SSLWantReadErrorSSLWantWriteError

在任何时候,重新协商是可能的,对 read() 的调用也可以引起写操作。

在 3.5 版更改: 每当接收或发送字节时,套接字超时不再复位。套接字超时现在是读取到 len 字节的最大总持续时间。

3.6 版后已移除: 使用 recv() 而不是 read()

SSLSocket.write(buf)

buf 写入SSL套接字并返回写入的字节数。 buf 参数必须是支持缓冲区接口的对象。

如果套接字是 非阻塞,写入将阻塞,则升高 SSLWantReadErrorSSLWantWriteError

因为在任何时候重新协商是可能的,对 write() 的调用也可以引起读取操作。

在 3.5 版更改: 每当接收或发送字节时,套接字超时不再复位。套接字超时现在是写入 buf 的最大总持续时间。

3.6 版后已移除: 使用 send() 而不是 write()

注解

read()write() 方法是读取和写入未加密的应用程序级数据并将其解密/加密到加密的线路级数据的低级方法。这些方法需要有效的SSL连接,即握手完成,SSLSocket.unwrap() 未被调用。

通常你应该使用套接字API方法,如 recv()send(),而不是这些方法。

SSLSocket.do_handshake()

执行SSL设置握手。

在 3.4 版更改: 握手方法还在套接字的 contextcheck_hostname 属性为真时执行 match_hostname()

在 3.5 版更改: 每当接收或发送字节时,套接字超时不再复位。套接字超时现在是握手的最大总持续时间。

SSLSocket.getpeercert(binary_form=False)

如果连接的另一端没有对等体的证书,则返回 None。如果SSL握手尚未完成,请提高 ValueError

如果 binary_form 参数是 False,并且从对等体接收到证书,则此方法返回 dict 实例。如果未验证证书,则dict为空。如果证书被验证,它返回一个具有几个密钥的dict,其中包括 subject (颁发证书的主体)和 issuer (颁发证书的主体)。如果证书包含 主题备用名称 扩展的实例(参见 RFC 3280),则字典中还将有一个 subjectAltName 密钥。

subjectissuer 字段是包含在相应字段的证书的数据结构中给出的相对可分辨名称(RDN)的序列的元组,并且每个RDN是名称 - 值对的序列。这里是一个真实世界的例子:

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

注解

要验证特定服务的证书,您可以使用 match_hostname() 功能。

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

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

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

在 3.2 版更改: 返回的字典包括附加项目,例如 issuernotBefore

在 3.4 版更改: 当握手未完成时,ValueError 上升。返回的字典包括附加的X509v3扩展项,例如 crlDistributionPointscaIssuersOCSP URI。

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

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

3.5 新版功能.

SSLSocket.pending()

返回可用于读取的已解密字节数,在连接上等待处理。

SSLSocket.context

与此SSL套接字绑定的 SSLContext 对象。如果使用顶级 wrap_socket() 函数(而不是 SSLContext.wrap_socket())创建SSL套接字,则这是为此SSL套接字创建的自定义上下文对象。

3.2 新版功能.

SSLSocket.server_side

一个布尔值,它是服务器端套接字的 True 和客户端套接字的 False

3.2 新版功能.

SSLSocket.server_hostname

服务器的主机名:str 类型或服务器端套接字的 None,或者在构造函数中未指定主机名。

3.2 新版功能.

SSLSocket.session

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

3.6 新版功能.

SSLSocket.session_reused

3.6 新版功能.

18.2.3. SSL上下文

3.2 新版功能.

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

class ssl.SSLContext(protocol=PROTOCOL_TLS)

创建一个新的SSL上下文。您可以传递 protocol,它必须是本模块中定义的 PROTOCOL_* 常量之一。 PROTOCOL_TLS 目前建议最大的互操作性和默认值。

参见

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

在 3.6 版更改: 上下文使用安全默认值创建。默认设置选项 OP_NO_COMPRESSIONOP_CIPHER_SERVER_PREFERENCEOP_SINGLE_DH_USEOP_SINGLE_ECDH_USEOP_NO_SSLv2 (除 PROTOCOL_SSLv2 之外)和 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 中的更多信息,请参阅 证书 的讨论。

password 参数可以是调用以获得用于解密私钥的密码的函数。仅当私钥被加密并且需要密码时才会调用它。它将被调用没有参数,它应该返回一个字符串,字节或bytearray。如果返回值是一个字符串,它将被编码为UTF-8,然后使用它来解密密钥。或者,字符串,字节或bytearray值可以直接作为 password 参数提供。如果私钥未加密并且不需要密码,则会被忽略。

如果未指定 password 参数并需要密码,则OpenSSL的内置密码提示机制将用于交互式提示用户输入密码。

如果私钥与证书不匹配,则引发 SSLError

在 3.3 版更改: 新的可选参数 password

SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)

从默认位置加载一组默认的“证书颁发机构”(CA)证书。在Windows上,它从 CAROOT 系统商店加载CA证书。在其他系统上,它调用 SSLContext.set_default_verify_paths()。在将来,该方法也可以从其他位置加载CA证书。

purpose 标志指定加载哪种CA证书。默认设置 Purpose.SERVER_AUTH 加载对TLS Web服务器身份验证(客户端套接字)标记和信任的证书。 Purpose.CLIENT_AUTH 在服务器端加载用于客户端证书验证的CA证书。

3.4 新版功能.

SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)

加载一组“认证中心”(CA)证书,用于在 verify_mode 不是 CERT_NONE 时验证其他对等体的证书。必须指定 cafilecapath 中的至少一个。

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

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

capath 字符串(如果存在)是在 OpenSSL特定布局 之后包含PEM格式的多个CA证书的目录的路径。

cadata 对象(如果存在)是一个或多个PEM编码证书的ASCII字符串或DER编码证书的 bytes-like object。与 capath 一样,PEM编码证书周围的额外行被忽略,但至少必须存在一个证书。

在 3.4 版更改: 新的可选参数 cadata

SSLContext.get_ca_certs(binary_form=False)

获取加载的“证书颁发机构”(CA)证书的列表。如果 binary_form 参数是 False,则每个列表条目是类似于 SSLSocket.getpeercert() 的输出的dict。否则,该方法返回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及更高版本上,cipher dict包含其他字段:
>>> ctx.get_ciphers()  # OpenSSL 1.1+
[{'aead': True,
  'alg_bits': 256,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(256) Mac=AEAD',
  'digest': None,
  'id': 50380848,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES256-GCM-SHA384',
  'protocol': 'TLSv1.2',
  'strength_bits': 256,
  'symmetric': 'aes-256-gcm'},
 {'aead': True,
  'alg_bits': 128,
  'auth': 'auth-rsa',
  'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=RSA  '
                 'Enc=AESGCM(128) Mac=AEAD',
  'digest': None,
  'id': 50380847,
  'kea': 'kx-ecdhe',
  'name': 'ECDHE-RSA-AES128-GCM-SHA256',
  'protocol': 'TLSv1.2',
  'strength_bits': 128,
  'symmetric': 'aes-128-gcm'}]

可用性:OpenSSL 1.0.2+

3.6 新版功能.

SSLContext.set_default_verify_paths()

从构建OpenSSL库时定义的文件系统路径加载一组默认的“证书颁发机构”(CA)证书。不幸的是,没有简单的方法来知道这个方法是否成功:如果没有找到证书,则不返回错误。当OpenSSL库作为操作系统的一部分提供时,它很可能被正确配置。

SSLContext.set_ciphers(ciphers)

设置使用此上下文创建的套接字的可用密码。它应该是 OpenSSL加密列表格式 中的字符串。如果不能选择密码(因为编译时选项或其他配置禁止使用所有指定的密码),将会引发 SSLError

注解

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

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+将中止握手,并提出 SSLError,当双方都支持ALPN,但不能同意一个协议。

3.5 新版功能.

SSLContext.set_npn_protocols(protocols)

指定在SSL/TLS握手期间套接字应通告哪些协议。它应该是一个字符串列表,如 ['http/1.1', 'spdy/2'],按首选顺序排列。协议的选择将在握手期间发生,并将根据 NPN草案规范 进行播放。握手成功后,SSLSocket.selected_npn_protocol() 方法将返回约定的协议。

如果 HAS_NPN 为False,此方法将提高 NotImplementedError

3.3 新版功能.

SSLContext.set_servername_callback(server_name_callback)

注册在TLS客户端Hello握手消息已被SSL/TLS服务器接收到时将调用的回调函数,当TLS客户端指定服务器名称指示时。服务器名称指示机制在 RFC 6066 第3节 - 服务器名称指示中指定。

每个 SSLContext 只能设置一个回调。如果 server_name_callbackNone,则回调被禁用。以后调用此函数将禁用先前注册的回调。

回调函数 server_name_callback 将使用三个参数调用;第一个是 ssl.SSLSocket,第二个是表示客户端打算通信的服务器名称的字符串(如果TLS客户端Hello不包含服务器名称,则为 None),第三个参数是原始 SSLContext。服务器名称参数是IDNA解码的服务器名称。

一个典型的使用这种回调是对 ssl.SSLSocketSSLSocket.context 属性更改为代表相匹配的服务器名称的证书链型 SSLContext 的新对象。

由于TLS连接的早期协商阶段,只能使用有限的方法和属性,如 SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert()SSLSocket.getpeercert()SSLSocket.cipher()SSLSocket.compress() 方法要求TLS连接已经超过TLS客户端Hello,因此将不包含返回有意义的值,也不能安全地调用。

server_name_callback 功能必须返回 None 以允许TLS协商继续。如果需要TLS故障,可以返回常量 ALERT_DESCRIPTION_*。其他返回值将导致与 ALERT_DESCRIPTION_INTERNAL_ERROR 的TLS致命错误。

如果服务器名称上存在IDNA解码错误,则TLS连接将终止,并向客户端发送 ALERT_DESCRIPTION_INTERNAL_ERROR 致命TLS警报消息。

如果从 server_name_callback 功能引发异常,TLS连接将以致命的TLS警报消息 ALERT_DESCRIPTION_HANDSHAKE_FAILURE 终止。

如果OpenSSL库在构建时定义了OPENSSL_NO_TLSEXT,则此方法将引发 NotImplementedError

3.4 新版功能.

SSLContext.load_dh_params(dhfile)

加载密钥生成参数用于Diffie-Helman(DH)密钥交换。使用DH密钥交换以牺牲计算资源(在服务器和客户端上)为代价提高了前向保密性。 dhfile 参数应该是包含PEM格式的DH参数的文件的路径。

此设置不适用于客户端套接字。您还可以使用 OP_SINGLE_DH_USE 选项进一步提高安全性。

3.3 新版功能.

SSLContext.set_ecdh_curve(curve_name)

为基于椭圆曲线的Diffie-Hellman(ECDH)密钥交换设置曲线名称。 ECDH显着快于正常DH,可以说是安全的。 curve_name 参数应该是描述众所周知的椭圆曲线的字符串,例如广泛支持的曲线的 prime256v1

此设置不适用于客户端套接字。您还可以使用 OP_SINGLE_ECDH_USE 选项进一步提高安全性。

如果 HAS_ECDHFalse,则此方法不可用。

3.3 新版功能.

参见

SSL/TLS和完全转发保密

文森特·伯纳特。

SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=None, session=None)

包装一个现有的Python套接字 sock 并返回一个 SSLSocket 对象。 sock 必须是 SOCK_STREAM 套接字;不支持其他套接字类型。

返回的SSL套接字与上下文,其设置和证书绑定。参数 server_sidedo_handshake_on_connectsuppress_ragged_eofs 具有与顶层 wrap_socket() 函数中相同的含义。

在客户端连接上,可选参数 server_hostname 指定我们要连接的服务的主机名。这允许单个服务器托管具有不同证书的多个基于SSL的服务,这与HTTP虚拟主机非常相似。如果 server_side 为真,指定 server_hostname 将产生 ValueError

session,参见 session

在 3.5 版更改: 始终允许传递server_hostname,即使OpenSSL没有SNI。

在 3.6 版更改: 添加了 session 参数。

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

通过包装BIO对象 incomingoutgoing 创建一个新的 SSLObject 实例。 SSL例程将从输入BIO读取输入数据,并将数据写入输出BIO。

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

在 3.6 版更改: 添加了 session 参数。

SSLContext.session_stats()

获取由此上下文创建或管理的SSL会话的统计信息。返回一个字典,它将每个 一条信息 的名称映射到它们的数字值。例如,这里是自上下文创建以来在会话缓存中的命中和未命中的总数:

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

是否在 SSLSocket.do_handshake() 中将对等证书的主机名与 match_hostname() 相匹配。上下文的 verify_mode 必须设置为 CERT_OPTIONALCERT_REQUIRED,并且必须通过 server_hostnamewrap_socket() 才能匹配主机名。

例:

import socket, ssl

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
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 新版功能.

注解

此功能需要OpenSSL 0.9.8f或更高版本。

SSLContext.options

表示在此上下文中启用的一组SSL选项的整数。默认值为 OP_ALL,但您可以通过将它们ORing在一起来指定其他选项,如 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.protocol

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

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_NONECERT_OPTIONALCERT_REQUIRED 中的一个。

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

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

18.2.4. 证书

证书通常是公钥/私钥系统的一部分。在该系统中,每个 principal (其可以是机器,或人或组织)被分配唯一的两部分加密密钥。关键的一部分是公共的,被称为 公钥;另一部分保密,称为 私钥。这两个部分是相关的,因为如果你用一个部分加密消息,你可以解密它与其他部分,只要 与其他部分。

证书包含有关两个主体的信息。它包含 subject 的名称和主题的公钥。它还包含第二主体 issuer 的声明,即主体是他声称的主体,并且这确实是主体的公共密钥。发行人的声明是用发行人的私钥签署的,只有发行人知道。然而,任何人都可以通过找到发行者的公钥,用它解密该语句,并将其与证书中的其他信息进行比较来验证发行者的声明。证书还包含有效时间段的信息。这被表示为两个字段,称为“notBefore”和“notAfter”。

在Python使用证书中,客户端或服务器可以使用证书来证明他们是谁。还可能需要网络连接的另一侧来产生证书,并且该证书可以被验证为满足需要这种验证的客户端或服务器。如果验证失败,可以将连接尝试设置为引发异常。验证由底层OpenSSL框架自动完成;应用程序不需要关心其机制。但是应用程序通常需要提供一组证书来允许此过程发生。

Python使用文件来包含证书。它们应该格式化为“PEM”(参见 RFC 1422),其是用标题行和页脚行包裹的基本64编码形式:

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

18.2.4.1. 证书链

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

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

18.2.4.2. CA证书

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

18.2.4.3. 组合键和证书

通常私钥存储在与证书相同的文件中;在这种情况下,只需要传递 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-----

18.2.4.4. 自签名证书

如果要创建提供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
%

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

18.2.5. 例子

18.2.5.1. 测试SSL支持

要在Python安装中测试SSL支持的存在,用户代码应使用以下习语:

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

18.2.5.2. 客户端操作

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

>>> context = ssl.create_default_context()

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

>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS)
>>> context.verify_mode = ssl.CERT_REQUIRED
>>> context.check_hostname = True
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")

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

当使用上下文连接到服务器时,CERT_REQUIRED 验证服务器证书:它确保服务器证书使用其中一个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.python.org'),
                    ('DNS', 'docs.python.org'),
                    ('DNS', 'testpypi.python.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'']

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

18.2.5.3. 服务器端操作

对于服务器操作,通常您需要在文件中具有服务器证书和私钥。您将首先创建一个包含密钥和证书的上下文,以便客户端可以检查您的真实性。然后你将打开一个套接字,绑定到一个端口,调用 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

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

18.2.6. 非阻塞套接字的注意事项

SSL套接字在非阻塞模式下与常规套接字稍有不同。当使用非阻塞套接字时,有几个事情你需要注意:

  • 如果I/O操作将阻塞,大多数 SSLSocket 方法将提高 SSLWantWriteErrorSSLWantReadError 而不是 BlockingIOError。如果需要对底层套接字进行读操作,则将引发 SSLWantReadError,并且对底层套接字上的写操作使用 SSLWantWriteError。注意,尝试 write 到SSL套接字可能首先需要来自底层套接字的 reading,并且从SSL套接字尝试 read 可能需要先前的 write 到底层套接字。

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

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

  • 相反,由于SSL层具有其自己的成帧,所以SSL套接字仍然可以具有可用于读取的数据,而没有 select() 意识到它。因此,您应首先调用 SSLSocket.recv() 以排除任何可能可用的数据,然后仅在仍需要的情况下阻止 select() 呼叫。

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

  • SSL握手本身将是非阻塞的:必须重试 SSLSocket.do_handshake() 方法,直到它成功返回。这里是一个使用 select() 等待套接字准备的概要:

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

参见

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

18.2.7. 内存BIO支持

3.5 新版功能.

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

  • SSL协议处理

  • 网络IO

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

组合SSL协议处理和网络IO通常工作良好,但有些情况下它不会。一个例子是异步IO框架,它希望使用与 socket.socket 和内部OpenSSL套接字IO例程采用的“基于文件描述符的选择/轮询(基于准备就绪)”模型不同的IO复用模型。这主要与Windows等平台相关,这种模式效率不高。为此,提供称为 SSLObjectSSLSocket 的缩小范围变体。

class ssl.SSLObject

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

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

可以使用 wrap_bio() 方法创建 SSLObject 实例。此方法将创建 SSLObject 实例并将其绑定到一对BIO。 incoming BIO用于将数据从Python传递到SSL协议实例,而 outgoing BIO用于以其他方式传递数据。

可以使用以下方法:

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

  • 任何形式的网络IO包括方法,如 recv()send()

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

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

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

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

与使用 SSLObject 相关的一些注意事项:

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

class ssl.MemoryBIO

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

pending

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

eof

指示存储器BIO在文件结束位置是否为当前的布尔值。

read(n=-1)

从内存缓冲区中读取 n 字节。如果未指定 n 或否定,则返回所有字节。

write(buf)

将字节从 buf 写入存储器BIO。 buf 参数必须是支持缓冲区协议的对象。

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

write_eof()

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

18.2.8. SSL会话

3.6 新版功能.

class ssl.SSLSession

session 使用的会话对象。

id
time
timeout
ticket_lifetime_hint
has_ticket

18.2.9. 安全注意事项

18.2.9.1. 最佳默认值

对于 客户端使用,如果您对安全策略没有任何特殊要求,强烈建议您使用 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上下文,则默认情况下不会启用证书验证或主机名检查。如果您这样做,请阅读以下段落以获得良好的安全级别。

18.2.9.2. 手动设置

18.2.9.2.1. 验证证书

当直接调用 SSLContext 构造函数时,CERT_NONE 是默认值。由于它不验证其他对等体,它可能是不安全的,特别是在客户端模式,大多数时候,你想要确保你正在谈话的服务器的真实性。因此,在客户端模式下,强烈建议使用 CERT_REQUIRED。然而,它本身是不够的;您还必须检查可以通过调用 SSLSocket.getpeercert() 获取的服务器证书与所需的服务匹配。对于许多协议和应用程序,服务可以由主机名标识;在这种情况下,可以使用 match_hostname() 功能。当启用 SSLContext.check_hostname 时,将自动执行此通用检查。

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

注解

在客户端模式下,除非启用匿名密码(默认情况下禁用),否则 CERT_OPTIONALCERT_REQUIRED 是等效的。

18.2.9.2.2. 协议版本

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

>>> client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
>>> client_context.options |= ssl.OP_NO_TLSv1
>>> client_context.options |= ssl.OP_NO_TLSv1_1

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

18.2.9.2.3. 密码选择

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

18.2.9.3. 多处理

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