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

源代码: Lib / ssl.py

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

注意

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

警告

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

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

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

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

18.2.1. Functions, Constants, and Exceptions

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

在尝试读取或写入数据时,由non-blocking SSL socket引发的SSLError的子类,但是在请求之前需要在基础TCP传输上接收更多数据满足。

版本3.3中的新功能。

exception ssl.SSLWantWriteError

尝试读取或写入数据时,由non-blocking SSL socket引发的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. Socket creation

以下函数允许创建独立套接字。从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.socketsock必须是SOCK_STREAM套接字;不支持其他套接字类型。

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

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

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

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

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

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

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

client / server SSLv2 SSLv3 SSLv23 TLSv1 TLSv1.1 TLSv1.2
SSLv2 yes no yes no no no
SSLv3 no yes yes no no no
SSLv23 no yes yes yes yes yes
TLSv1 no no yes yes no no
TLSv1.1 no no yes no yes no
TLSv1.2 no no yes no no yes

注意

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

密码参数设置此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中更改:新的可选参数密码

18.2.1.2. Context creation

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

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

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

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

设置为:具有不带RC4和无认证密码套件的高加密密码套件的PROTOCOL_SSLv23OP_NO_SSLv2OP_NO_SSLv3SERVER_AUTH作为目的verify_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从默认密码字符串中删除。

18.2.1.3. Random generation

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

ssl.RAND_status()

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

ssl.RAND_egd(path)

如果您在某处运行熵收集守护程序(EGD),而路径是打开的套接字连接的路径名,则将从套接字读取256个字节的随机性,并将其添加到SSL伪随机数生成器增加生成的密钥的安全性。这通常仅在没有更好的随机源的系统上是必需的。

有关熵收集守护程序的来源,请参见http://egd.sourceforge.net/http://prngd.sourceforge.net/

可用性:不可用于LibreSSL。

ssl.RAND_add(bytes, entropy)

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

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

18.2.1.4. Certificate handling

ssl.match_hostname(cert, hostname)

验证cert(以SSLSocket.getpeercert()返回的解码格式)与给定的主机名匹配。所应用的规则是用于检查 RFC 2818 RFC 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 ”中的证书,给出表示“notBefore”或“notAfter”日期的cert_time字符串, %d %H:%M:%S %Y %Z“ strptime格式。

这里有一个例子:

>>> 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_SSLv23, ca_certs=None)

给定受SSL保护的服务器的地址addr作为(主机名端口号)对,提取服务器的证书,并返回它作为PEM编码的字符串。如果指定ssl_version,则使用该版本的SSL协议尝试连接到服务器。如果指定ca_certs,它应该是一个包含根证书列表的文件,其格式与wrap_socket()中相同的参数相同。该调用将尝试根据该组根证书验证服务器证书,并且如果验证尝试失败,则失败。

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

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

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目录的路径

版本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. Constants

ssl.CERT_NONE

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

请参阅下面的Security considerations

ssl.CERT_OPTIONAL

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

Use of this setting requires a valid set of CA certificates to be passed, either to SSLContext.load_verify_locations() or as a value of the ca_certs parameter to wrap_socket().

ssl.CERT_REQUIRED

SSLContext.verify_modecert_reqs参数到wrap_socket()的可能值。在此模式下,需要从套接字连接的另一侧获取证书;如果未提供证书,或者其验证失败,则会引发SSLError

Use of this setting requires a valid set of CA certificates to be passed, either to SSLContext.load_verify_locations() or as a value of the ca_certs parameter to wrap_socket().

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

ssl.PROTOCOL_SSLv23

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

ssl.PROTOCOL_SSLv2

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

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

警告

SSLv2已是不安全的协议。除非你知道自己在做什么,否则不要使用该协议。

ssl.PROTOCOL_SSLv3

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

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

警告

SSLv3已是不安全的协议。除非你知道自己在做什么,否则不要使用该协议。

ssl.PROTOCOL_TLSv1

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

ssl.PROTOCOL_TLSv1_1

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

版本3.4中的新功能。

ssl.PROTOCOL_TLSv1_2

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

版本3.4中的新功能。

ssl.OP_ALL

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

版本3.2中的新功能。

ssl.OP_NO_SSLv2

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

版本3.2中的新功能。

ssl.OP_NO_SSLv3

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

版本3.2中的新功能。

ssl.OP_NO_TLSv1

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

版本3.2中的新功能。

ssl.OP_NO_TLSv1_1

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

版本3.4中的新功能。

ssl.OP_NO_TLSv1_2

阻止TLSv1.2连接。此选项仅适用于PROTOCOL_SSLv23它防止对等体选择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中的新功能。

ssl.HAS_ALPN

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

版本3.5中的新功能。

ssl.HAS_ECDH

OpenSSL库是否具有内建支持基于椭圆曲线的Diffie-Hellman密钥交换。除非特征被分发器显式禁用,否则应该为true。

版本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中的新功能。

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

18.2.2. SSL Sockets

class ssl.SSLSocket(socket.socket)

SSL套接字提供Socket Objects的以下方法:

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

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

在版本3.5中已更改:添加了sendfile()方法。

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

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

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

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

引发SSLWantReadErrorSSLWantWriteError,如果套接字non-blocking,并且读取将阻塞。

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

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

SSLSocket.write(buf)

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

引发SSLWantReadErrorSSLWantWriteError(如果套接字non-blocking),写入将阻塞。

在任何时候,重新协商都是可能的,对write()的调用也可能导致读操作。

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

注意

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

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

SSLSocket.do_handshake()

执行SSL设置握手。

Changed in version 3.4: The handshake method also performs match_hostname() when the check_hostname attribute of the socket’s context is true.

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

SSLSocket.getpeercert(binary_form=False)

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

如果binary_form参数为False,并且从对等体接收到证书,则此方法返回dict实例。如果未验证证书,则dict为空。If the certificate was validated, it returns a dict with several keys, amongst them subject (the principal for which the certificate was issued) and issuer (the principal issuing the certificate). 如果证书包含主题备用名称扩展的实例(请参阅 RFC 3280),则还会有subjectAltName

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

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

注意

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

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

  • 对于客户端SSL套接字,服务器将始终提供证书,而不管是否需要验证;
  • for a server SSL socket, the client will only provide a certificate when requested by the server; therefore getpeercert() will return None if you used CERT_NONE (rather than CERT_OPTIONAL or CERT_REQUIRED).

在版本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中的新功能。

18.2.3. SSL Contexts

版本3.2中的新功能。

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

class ssl.SSLContext(protocol)

创建一个新的SSL上下文。您必须传递协议,该协议必须是本模块中定义的PROTOCOL_*常量之一。PROTOCOL_SSLv23目前推荐用于最大的互操作性。

也可以看看

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

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的讨论。

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

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

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

在版本3.3中更改:新的可选参数密码

SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)

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

目的标志指定加载何种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)证书。必须指定cafilecapath中的至少一个。

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

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

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

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

版本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,第二个是表示客户端打算通信的服务器名称的字符串(或None,如果TLS客户端Hello不包含服务器名称),第三个参数是原始的SSLContext服务器名称参数是IDNA解码的服务器名称。

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

由于TLS连接的早期协商阶段,只能使用有限的方法和属性,例如SSLSocket.selected_alpn_protocol()SSLSocket.contextSSLSocket.getpeercert()SSLSocket.getpeercert()SSLSocket.cipher()SSLSocket.compress()

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

If there is an IDNA decoding error on the server name, the TLS connection will terminate with an ALERT_DESCRIPTION_INTERNAL_ERROR fatal TLS alert message to the client.

如果从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_ECDH为False,则此方法不可用。

版本3.3中的新功能。

也可以看看

SSL / TLS&amp;完美转发保密
Vincent Bernat。
SSLContext.wrap_socket(sock, server_side=False, do_handshake_on_connect=True, suppress_ragged_eofs=True, server_hostname=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

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

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

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

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

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_hostname传递给wrap_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

SSLContext.protocol

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

SSLContext.verify_flags

证书验证操作的标志。您可以将VERIFY_CRL_CHECK_LEAF的标志设为OR运算来设定。默认情况下,OpenSSL既不需要也不验证证书吊销列表(CRL)。仅适用于openssl版本0.9.8+。

版本3.4中的新功能。

SSLContext.verify_mode

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

18.2.4. Certificates

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

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

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

Python使用文件来包含证书。它们应该格式化为“PEM”(请参阅​​ RFC 1422),它是一个用标题行和页脚行包装的base-64编码格式:

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

18.2.4.1. Certificate chains

包含证书的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-----

18.2.4.2. CA certificates

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

18.2.4.3. Combined key and certificate

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

-----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. Self-signed certificates

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

18.2.5.1. Testing for SSL support

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

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

18.2.5.2. Client-side operation

此示例创建具有客户端套接字的建议安全设置的SSL上下文,包括自动证书验证:

>>> context = ssl.create_default_context()

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

>>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
>>> 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'']

请参阅下面的Security considerations

18.2.5.3. Server-side operation

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

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

18.2.6. Notes on non-blocking sockets

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

  • 如果I / O操作会阻塞,大多数SSLSocket方法将引发SSLWantWriteErrorSSLWantReadError而不是BlockingIOError如果底层套接字上的读取操作是必要的,则会产生SSLWantReadError,而对于底层套接字上的写入操作,则会产生SSLWantWriteError请注意,尝试写入SSL套接字可能需要先从底层套接字读取,并尝试从SSL套接字读取先前写入到底层套接字。

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

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

  • 相反,由于SSL层具有自己的成帧,因此SSL套接字仍然可以具有可用于读取的数据,而没有select()意识到它。因此,您应该首先调用SSLSocket.recv()以排除任何可能的可用数据,然后只在有必要时阻塞select()

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

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

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

也可以看看

asyncio模块支持non-blocking SSL sockets,并提供更高级别的API。它使用selectors模块轮询事件,并处理SSLWantWriteErrorSSLWantReadErrorBlockingIOError异常。它也以异步方式运行SSL握手。

18.2.7. Memory BIO Support

版本3.5中的新功能。

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

  • SSL协议处理
  • 网络IO

网络IO API与socket.socket提供的相同,SSLSocket也继承。这允许SSL套接字用作普通套接字的插入式替换,使得很容易向现有应用程序添加SSL支持。

组合SSL协议处理和网络IO通常工作得很好,但有些情况下它不会。一个例子是异步IO框架,它希望使用一个不同的IO复用模型,而不是由socket.socket假设的“select / poll on a file描述器”模型OpenSSL套接字IO例程。这主要与Windows等平台相关,这种模式效率不高。为此,提供了SSLSocket的缩小范围变体,称为SSLObject

class ssl.SSLObject

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

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

可以使用wrap_bio()方法创建SSLObject实例。此方法将创建SSLObject实例并将其绑定到一对BIO。传入 BIO用于将数据从Python传递到SSL协议实例,而传出 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. Security considerations

18.2.8.1. Best defaults

对于客户端使用,如果您对安全策略没有任何特殊要求,强烈建议您使用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.8.2. Manual settings

18.2.8.2.1. Verifying certificates

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

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

Note

In client mode, CERT_OPTIONAL and CERT_REQUIRED are equivalent unless anonymous ciphers are enabled (they are disabled by default).

18.2.8.2.2. Protocol versions

SSL版本2和3被视为不安全,因此使用起来很危险。如果希望客户端和服务器之间达到最大兼容性,建议使用PROTOCOL_SSLv23作为协议版本,然后使用SSLContext.options属性显式禁用SSLv2和SSLv3:

context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3

上面创建的SSL上下文将仅允许TLSv1和更高版本(如果系统支持)连接。

18.2.8.2.3. Cipher selection

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

18.2.8.3. Multi-processing

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