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。
警告
请勿在未阅读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子模块的字符串助记符,例如
SSL
,PEM
或X509
。可能值的范围取决于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中的新功能。
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.socket
sock
必须是SOCK_STREAM
套接字;不支持其他套接字类型。对于客户端套接字,上下文构造是惰性的;如果底层套接字尚未连接,则将在套接字上调用
connect()
之后执行上下文构造。对于服务器端套接字,如果套接字没有远程对等端,则假定它是侦听套接字,并且服务器端SSL包装是通过accept()
方法接受的客户端连接自动执行的。wrap_socket()
可能引发SSLError
。keyfile
和certfile
参数指定包含要用于标识连接的本地端的证书的可选文件。有关如何将证书存储在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
构造函数时更高的安全级别。cafile,capath,cadata代表可信的证书验证CA证书,如
SSLContext.load_verify_locations()
如果三个都是None
,此功能可以选择信任系统的默认CA证书。设置为:具有不带RC4和无认证密码套件的高加密密码套件的
PROTOCOL_SSLv23
,OP_NO_SSLv2
和OP_NO_SSLv3
。将SERVER_AUTH
作为目的将verify_mode
设置为CERT_REQUIRED
,并加载CA证书(至少有一个cafile,capath或cadata)或使用SSLContext.load_default_certs()
加载默认CA证书。注意
如果您发现当某些较旧的客户端或服务器尝试使用此函数创建的
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方法不支持操作,则引发
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中的新功能。
-
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”日期的
strptime格式。cert_time
字符串, %d %H:%M:%S %Y %Z“这里有一个例子:
>>> 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_version从
PROTOCOL_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 tupleDefaultVerifyPaths
: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可以是
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
。如果证书对于所有目的都是可信赖的,则信任将证书的目的指定为一组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可以是
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
。可用性:Windows。
版本3.4中的新功能。
18.2.1.5. Constants¶
-
ssl.
CERT_NONE
¶ SSLContext.verify_mode
或cert_reqs
参数到wrap_socket()
的可能值。在此模式下(默认),将不需要来自套接字连接另一端的证书。如果从另一端接收到证书,则不尝试验证它。请参阅下面的Security considerations。
-
ssl.
CERT_OPTIONAL
¶ SSLContext.verify_mode
或cert_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 theca_certs
parameter towrap_socket()
.
-
ssl.
CERT_REQUIRED
¶ SSLContext.verify_mode
或cert_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 theca_certs
parameter towrap_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_ECDH
¶ OpenSSL库是否具有内建支持基于椭圆曲线的Diffie-Hellman密钥交换。除非特征被分发器显式禁用,否则应该为true。
版本3.3中的新功能。
-
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的以下方法:
accept()
bind()
close()
connect()
detach()
fileno()
getpeername()
,getsockname()
getsockopt()
,setsockopt()
gettimeout()
,settimeout()
,setblocking()
listen()
makefile()
recv()
,recv_into()
(但不允许传递非零flags
参数)send()
,sendall()
(具有相同的限制)sendfile()
(但os.sendfile
将仅用于纯文本套接字,否则将使用send()
shutdown()
然而,由于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,则读入缓冲区,并返回读取的字节数。引发
SSLWantReadError
或SSLWantWriteError
,如果套接字non-blocking,并且读取将阻塞。在任何时候都可以重新协商,调用
read()
也可以引起写操作。在版本3.5中更改:套接字超时在每次接收或发送字节时不再复位。套接字超时现在是读取到len字节的最大总持续时间。
-
SSLSocket.
write
(buf)¶ 将buf写入SSL套接字并返回写入的字节数。buf参数必须是支持缓冲区接口的对象。
引发
SSLWantReadError
或SSLWantWriteError
(如果套接字non-blocking),写入将阻塞。在任何时候,重新协商都是可能的,对
write()
的调用也可能导致读操作。在版本3.5中更改:套接字超时在每次接收或发送字节时不再复位。套接字超时现在是写入buf的最大总持续时间。
注意
read()
和write()
方法是读取和写入未加密的应用程序级数据和解密/加密到加密的线程级数据。这些方法需要有效的SSL连接,即握手已完成,并且未调用SSLSocket.unwrap()
。
-
SSLSocket.
do_handshake
()¶ 执行SSL设置握手。
Changed in version 3.4: The handshake method also performs
match_hostname()
when thecheck_hostname
attribute of the socket’scontext
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 themsubject
(the principal for which the certificate was issued) andissuer
(the principal issuing the certificate). 如果证书包含主题备用名称扩展的实例(请参阅 RFC 3280),则还会有subjectAltName
subject
和issuer
字段是包含在相应字段的证书的数据结构中给出的相对可分辨名称(RDN)的序列的元组,并且每个RDN是名称序列值对。这里是一个现实世界的例子:{'issuer': ((('countryName', 'IL'),), (('organizationName', 'StartCom Ltd.'),), (('organizationalUnitName', 'Secure Digital Certificate Signing'),), (('commonName', 'StartCom Class 2 Primary Intermediate Server CA'),)), 'notAfter': 'Nov 22 08:15:19 2013 GMT', 'notBefore': 'Nov 21 03:09:52 2011 GMT', 'serialNumber': '95F0', 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),), (('countryName', 'US'),), (('stateOrProvinceName', 'California'),), (('localityName', 'San Francisco'),), (('organizationName', 'Electronic Frontier Foundation, Inc.'),), (('commonName', '*.eff.org'),), (('emailAddress', '[email protected]'),)), 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')), 'version': 3}
注意
要验证特定服务的证书,您可以使用
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 returnNone
if you usedCERT_NONE
(rather thanCERT_OPTIONAL
orCERT_REQUIRED
).
在版本3.2中更改:返回的字典包含诸如
issuer
和notBefore
之类的其他项目。在版本3.4中更改:
ValueError
。返回的字典包括附加的X509v3扩展项,例如crlDistributionPoints
,caIssuers
和OCSP
URI。
-
SSLSocket.
cipher
()¶ 返回一个三值元组,其中包含所使用的密码的名称,定义其使用的SSL协议的版本以及正在使用的秘密位的数量。如果未建立连接,则返回
None
。
返回握手期间客户端共享的密码列表。返回的列表的每个条目是一个三值元组,包含密码的名称,定义其使用的SSL协议的版本以及密码使用的秘密位的数量。
shared_ciphers()
返回None
如果没有建立连接或套接字是客户端套接字。版本3.5中的新功能。
-
SSLSocket.
compression
()¶ 返回用作字符串的压缩算法,如果连接未压缩,则返回
None
。如果较高级协议支持其自己的压缩机制,则可以使用
OP_NO_COMPRESSION
禁用SSL级压缩。版本3.3中的新功能。
-
SSLSocket.
get_channel_binding
(cb_type="tls-unique")¶ 获取当前连接的通道绑定数据,作为字节对象。如果未连接或握手尚未完成,则返回
None
。cb_type参数允许选择所需的通道绑定类型。有效的通道绑定类型列在
CHANNEL_BINDING_TYPES
列表中。目前仅支持由 RFC 5929定义的'tls-unique'通道绑定。如果请求了不受支持的通道绑定类型,则会引发ValueError
。版本3.3中的新功能。
-
SSLSocket.
selected_alpn_protocol
()¶ 返回在TLS握手期间选择的协议。如果
SSLContext.set_alpn_protocols()
未被调用,如果另一方不支持ALPN,如果此套接字不支持任何客户端提议的协议,或者握手尚未发生,None
。版本3.5中的新功能。
-
SSLSocket.
selected_npn_protocol
()¶ 返回在TLS / SSL握手期间选择的更高级协议。如果未调用
SSLContext.set_npn_protocols()
,或者如果对方不支持NPN,或者握手尚未发生,则会返回None
。版本3.3中的新功能。
-
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中的新功能。
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上,它从
CA
和ROOT
系统存储加载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)证书。必须指定cafile或capath中的至少一个。此方法还可以以PEM或DER格式加载认证撤销列表(CRL)。为了使用CRL,必须正确配置
SSLContext.verify_flags
。cafile字符串(如果存在)是PEM格式的级联CA证书文件的路径。有关如何在此文件中排列证书的详细信息,请参阅Certificates的讨论。
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.
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_callback为None
,则回调将被禁用。在随后的时间调用此函数将禁用先前注册的回调。回调函数server_name_callback将使用三个参数调用;第一个是
ssl.SSLSocket
,第二个是表示客户端打算通信的服务器名称的字符串(或None
,如果TLS客户端Hello不包含服务器名称),第三个参数是原始的SSLContext
。服务器名称参数是IDNA解码的服务器名称。此回调的典型用法是将
ssl.SSLSocket
的SSLSocket.context
属性更改为表示证书的SSLContext
类型的新对象链匹配服务器名称。由于TLS连接的早期协商阶段,只能使用有限的方法和属性,例如
SSLSocket.selected_alpn_protocol()
和SSLSocket.context
。SSLSocket.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_side,do_handshake_on_connect和suppress_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_side和server_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_OPTIONAL
或CERT_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_NONE
,CERT_OPTIONAL
或CERT_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
方法将引发SSLWantWriteError
或SSLWantReadError
而不是BlockingIOError
如果底层套接字上的读取操作是必要的,则会产生SSLWantReadError
,而对于底层套接字上的写入操作,则会产生SSLWantWriteError
。请注意,尝试将写入SSL套接字可能需要先从底层套接字读取,并尝试从SSL套接字读取先前写入到底层套接字。在早期的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
模块支持non-blocking SSL sockets,并提供更高级别的API。它使用selectors
模块轮询事件,并处理SSLWantWriteError
,SSLWantReadError
和BlockingIOError
异常。它也以异步方式运行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用于以其他方式传递数据。提供以下方法:
context
server_side
server_hostname
read()
write()
getpeercert()
selected_npn_protocol()
cipher()
shared_ciphers()
compression()
pending()
do_handshake()
unwrap()
get_channel_binding()
与
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
上的所有IO为non-blocking。这意味着例如read()
将引发一个SSLWantReadError
,如果它需要更多的数据,比传入的BIO可用。- 没有像
wrap_socket()
的模块级wrap_bio()
调用。SSLObject
始终通过SSLContext
创建。
SSLObject使用内存缓冲区与外部世界通信。类MemoryBIO
提供了可用于此目的的内存缓冲区。它包装一个OpenSSL内存BIO(基本IO)对象:
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
andCERT_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¶
如果将此模块作为多处理应用程序(例如使用multiprocessing
或concurrent.futures
模块)的一部分,请注意OpenSSL的内部随机数生成器没有正确处理分叉进程。如果应用程序使用任何具有os.fork()
的SSL功能,应用程序必须更改父进程的PRNG状态。任何成功调用RAND_add()
,RAND_bytes()
或RAND_pseudo_bytes()
就足够了。
也可以看看
- 类
socket.socket
- 基础
socket
类的文档 - SSL / TLS加密加密:简介
- Apache Web服务器文档简介
- RFC 1422:Internet电子邮件的隐私增强:第II部分:基于证书的密钥管理
- 史蒂夫·肯特
- RFC 1750:安全性随机性建议
- D. Eastlake et。et al。
- RFC 3280:Internet X.509公钥基础设施证书和CRL配置文件
- Housley等人et al。
- RFC 4366:传输层安全性(TLS)扩展
- Blake-Wilson et。et al。
- RFC 5246:传输层安全(TLS)协议版本1.2
- T.Dierks et。et al。
- RFC 6066:传输层安全性(TLS)扩展
- D. Eastlake
- IANA TLS:传输层安全性(TLS)参数
- IANA