ssl

ssl — TLS/SSL wrapper for socket objects

2.6版本中的新功能。

源代码: Lib / ssl.py

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

注意

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

警告

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

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

这个模块提供了一个ssl.SSLSocketsocket.socket类型派生出来的类,它提供了一个类似套接字的包装器,它也使用SSL加密和解密通过套接字的数据。它支持其他方法,如getpeercert()检索连接另一端的证书,以及cipher()检索用于安全连接的密码。

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

1.函数,常量和例外

exception ssl.SSLError

引发了底层SSL实现的错误(目前由OpenSSL库提供)。这表示在底层网络连接上叠加的更高级别的加密和认证层中存在一些问题。这个错误是一个子类型socket.error,它又是一个子类型IOErrorSSLError实例的错误代码和消息由OpenSSL库提供。

library

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

2.7.9版本的新功能。

reason

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

2.7.9版本的新功能。

exception ssl.SSLZeroReturnError

SSLError尝试读取或写入时引发的子类以及SSL连接已被彻底关闭。请注意,这并不意味着底层传输(读取TCP)已关闭。

2.7.9版本的新功能。

exception ssl.SSLWantReadError

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

2.7.9版本的新功能。

exception ssl.SSLWantWriteError

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

2.7.9版本的新功能。

exception ssl.SSLSyscallError

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

2.7.9版本的新功能。

exception ssl.SSLEOFError

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

2.7.9版本的新功能。

exception ssl.CertificateError

引发一个证书错误(如主机名不匹配)。但是OpenSSL检测到的证书错误引发了一个问题SSLError

1.1。套接字创建

以下功能允许创建独立套接字。从Python 2.7.9开始,它可以更灵活地使用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)

需要一个实例socksocket.socket,并且返回的一个实例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_SSLv23; 它提供了与其他版本的最大兼容性。

下面是一个表格,显示客户端(侧面)中的哪些版本可以连接到服务器中的哪些版本(沿顶部):

client / serverSSLv2SSLv3SSLv23TLSv1TLSv1.1TLSv1.2
SSLv2yesnoyesnonono
SSLv3noyesyesnonono
SSLv23noyesyesyesyesyes
TLSv1nonoyesyesnono
TLSv1.1nonoyesnoyesno
TLSv1.2nonoyesnonoyes

注意

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

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

该参数do_handshake_on_connect指定在执行a之后socket.connect()是否自动执行SSL握手,或者是否通过调用SSLSocket.do_handshake()方法明确调用应用程序。调用SSLSocket.do_handshake()明确地让程序控制握手中涉及的套接字I / O的阻塞行为。

该参数suppress_ragged_eofs指定该SSLSocket.read()方法应如何从连接的另一端发出意外的EOF信号。如果指定为True(缺省值),它将返回一个正常的EOF(一个空字节对象),以响应从底层套接字引发的意外的EOF错误; 如果False它会将异常提交给调用者。

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

1.2。创建上下文

便利功能可帮助创建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证书。

这些设置是:PROTOCOL_SSLv23OP_NO_SSLv2并且OP_NO_SSLv3带有不带RC4的高加密密码套件,并且没有未经身份验证的密码套件。传递SERVER_AUTH目的设置verify_modeCERT_REQUIRED与任一负载CA证书(当中的至少一个凭证档案错误capathcadata给出),或者使用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

2.7.9版本的新功能。

在版本2.7.10中更改:RC4从默认密码字符串中删除。

在版本2.7.13中更改:ChaCha20 / Poly1305已添加到默认密码字符串。

3DES已从默认密码字符串中删除。

ssl._https_verify_certificates(enable=True)

指定创建客户端HTTPS连接时是否验证服务器证书,而不指定特定的SSL上下文。

从Python 2.7.9开始httplib,使用它的模块(比如urllib2xmlrpclib)默认验证在建立客户端HTTPS连接时收到的远程服务器证书。此默认验证会检查证书是否由系统信任库中的证书颁发机构签署,并且所提供的证书上的公用名称(或主题替代名称)与请求的主机相匹配。

设置启用True确保此默认行为有效。

设置启用False恢复默认的HTTPS证书处理直至Python 2.7.8及更早版本,使用自签名证书允许服务器的连接,使用由证书模板管理局系统信任存储中不存在签名证书的服务器及服务器主机名与提供的服务器证书不匹配。

这个函数的下划线表示它在Python 3的任何实现中并不存在,并且可能不会在所有Python 2.7实现中出现。在必要时绕过证书检查或系统信任存储的可移植方法是,工具通过显式传递适当配置的SSL上下文来逐个启用该方法,而不是恢复标准库客户机模块的默认行为。

2.7.12版本中的新功能。

1.3。随机生成

自2.7.13版弃用:OpenSSL已弃用ssl.RAND_pseudo_bytes(),请ssl.RAND_bytes()改用。

ssl.RAND_status()

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

ssl.RAND_egd(path)

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

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

ssl.RAND_add(bytes, entropy)

将给定的字节混合到SSL伪随机数生成器中。参数(一个浮点数)是包含在字符串中的的下限(所以你可以随时使用0.0)。有关源的更多信息,请参阅RFC 1750

1.4。证书处理

ssl.match_hostname(cert, hostname)

验证证书(以解码格式返回SSLSocket.getpeercert())匹配给定的主机名。所应用的规则是用于检查RFC 2818RFC 6125中概述的HTTPS服务器身份的规则,不同之处在于当前不支持IP地址。除了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'

2.7.9版本的新功能。

ssl.cert_time_to_seconds(cert_time)

返回从Epoch开始的秒数,给定cert_time表示来自以"%b %d %H:%M:%S %Y %Z"strptime格式(C区域设置)的证书的“notBefore”或“notAfter”日期的字符串。

这是一个例子:

>>> import ssl >>> timestamp = ssl.cert_time_to_seconds("Jan 5 09:34:43 2018 GMT") >>> timestamp 1515144883 >>> from datetime import datetime >>> print(datetime.utcfromtimestamp(timestamp)) 2018-01-05 09:34:43

“notBefore”或“notAfter”日期必须使用GMT(RFC 5280)。

2.7.9版中的更改:将输入时间解释为输入字符串中'GMT'时区指定的UTC时间。以前使用过本地时区。返回一个整数(输入格式中不含小数)

ssl.get_server_certificate(addr, ssl_version=PROTOCOL_SSLv23, ca_certs=None)

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

在版本2.7.9中更改:此功能现在与IPv6兼容,并且默认的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()。返回值是一个命名的元组 DefaultVerifyPaths

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

  • capath- 已解析的路径,或者None目录不存在,

  • openssl_cafile_env - 指向cafile的OpenSSL环境密钥,

  • openssl_cafile - 硬编码的cafile文件路径,

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

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

可用性:LibreSSL忽略环境变量openssl_cafile_envopenssl_capath_env

2.7.9版本的新功能。

ssl.enum_certificates(store_name)

从Windows的系统证书存储中检索证书。store_name可能是其中之一CAROOT或者MY。Windows也可能提供其他的证书存储。

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

例:

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

可用性:Windows。

2.7.9版本的新功能。

ssl.enum_crls(store_name)

从Windows的系统证书存储中检索CRL。store_name可能是其中之一CAROOT或者MY。Windows也可能提供其他的证书存储。

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

可用性:Windows。

2.7.9版本的新功能。

1.5。常量

ssl.CERT_NONE

可能的值SSLContext.verify_mode,或cert_reqs参数wrap_socket()。在这种模式下(默认),从套接字连接的另一端不需要证书。如果从另一端收到证书,则不会尝试对其进行验证。

请参阅下面关于安全性考虑的讨论。

ssl.CERT_OPTIONAL

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

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

ssl.CERT_REQUIRED

可能的值SSLContext.verify_mode,或cert_reqs参数wrap_socket()。在这种模式下,证书需要来自套接字连接的另一端; 一个SSLError如果验证失败,如果没有提供证书将提高,或。

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

ssl.VERIFY_DEFAULT

可能值SSLContext.verify_flags。在这种模式下,证书撤销列表(CRL)不会被检查。默认情况下,OpenSSL既不要求也不验证CRL。

2.7.9版本的新功能。

ssl.VERIFY_CRL_CHECK_LEAF

可能值SSLContext.verify_flags。在这种模式下,只有对等证书是检查而非中间CA证书。该模式需要由对等证书的颁发者(其直接祖先CA)签署的有效CRL。如果没有正确的加载SSLContext.load_verify_locations,验证将失败。

2.7.9版本的新功能。

ssl.VERIFY_CRL_CHECK_CHAIN

可能值SSLContext.verify_flags。在此模式下,检查对等证书链中所有证书的CRL。

2.7.9版本的新功能。

ssl.VERIFY_X509_STRICT

SSLContext.verify_flags为破损的X.509证书禁用解决方法的可能值。

2.7.9版本的新功能。

ssl.VERIFY_X509_TRUSTED_FIRST

可能值SSLContext.verify_flags。它指示OpenSSL在构建信任链以验证证书时优先使用可信证书。该标志默认启用。

2.7.10版中的新功能。

ssl.PROTOCOL_TLS

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

2.7.13版中的新功能。

ssl.PROTOCOL_SSLv23

别名PROTOCOL_TLS

自2.7.13版弃用:PROTOCOL_TLS代之以使用。

ssl.PROTOCOL_SSLv2

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

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

警告

SSL版本2不安全。它的使用非常令人沮丧。

自从2.7.13版开始弃用:OpenSSL已经取消了对SSLv2的支持。

ssl.PROTOCOL_SSLv3

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

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

警告

SSL版本3不安全。它的使用非常令人沮丧。

自2.7.13版弃用:OpenSSL已弃用所有版本特定的协议。使用带有标志的默认协议OP_NO_SSLv3

ssl.PROTOCOL_TLSv1

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

自2.7.13版弃用:OpenSSL已弃用所有版本特定的协议。使用带有标志的默认协议OP_NO_SSLv3

ssl.PROTOCOL_TLSv1_1

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

2.7.9版本的新功能。

自2.7.13版弃用:OpenSSL已弃用所有版本特定的协议。使用带有标志的默认协议OP_NO_SSLv3

ssl.PROTOCOL_TLSv1_2

选择TLS 1.2版作为通道加密协议。这是最现代化的版本,如果双方都能说出来,这可能是最大限度保护的最佳选择。仅适用于openssl 1.0.1+版本。

2.7.9版本的新功能。

自2.7.13版弃用:OpenSSL已弃用所有版本特定的协议。使用带有标志的默认协议OP_NO_SSLv3

ssl.OP_ALL

为其他SSL实现中存在的各种错误提供解决方法。该选项默认设置。它不一定将标志设置为OpenSSL的SSL_OP_ALL常量。

2.7.9版本的新功能。

ssl.OP_NO_SSLv2

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

2.7.9版本的新功能。

ssl.OP_NO_SSLv3

防止SSLv3连接。该选项仅适用于与PROTOCOL_SSLv23。它防止对等方选择SSLv3作为协议版本。

2.7.9版本的新功能。

ssl.OP_NO_TLSv1

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

2.7.9版本的新功能。

ssl.OP_NO_TLSv1_1

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

2.7.9版本的新功能。

ssl.OP_NO_TLSv1_2

阻止TLSv1.2连接。该选项仅适用于与PROTOCOL_SSLv23。它可以防止对等方选择TLSv1.2作为协议版本。仅适用于openssl 1.0.1+版本。

2.7.9版本的新功能。

ssl.OP_CIPHER_SERVER_PREFERENCE

使用服务器的密码排序首选项,而不是客户端的。此选项对客户端套接字和SSLv2服务器套接字没有影响。

2.7.9版本的新功能。

ssl.OP_SINGLE_DH_USE

防止对不同SSL会话重复使用相同的DH密钥。这提高了前向保密性,但需要更多的计算资源。该选项仅适用于服务器套接字。

2.7.9版本的新功能。

ssl.OP_SINGLE_ECDH_USE

防止重复使用相同的ECDH密钥以用于不同的SSL会话。这提高了前向保密性,但需要更多的计算资源。该选项仅适用于服务器套接字。

2.7.9版本的新功能。

ssl.OP_NO_COMPRESSION

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

该选项仅适用于OpenSSL 1.0.0及更高版本。

2.7.9版本的新功能。

ssl.HAS_ALPN

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

2.7.10版中的新功能。

ssl.HAS_ECDH

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

2.7.9版本的新功能。

ssl.HAS_SNI

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

2.7.9版本的新功能。

ssl.HAS_NPN

NPN草案规范中描述的OpenSSL库是否内置了对Next Protocol Negotiation的支持。如果为true,则可以使用该方法通知您想要支持的协议。SSLContext.set_npn_protocols()

2.7.9版本的新功能。

ssl.CHANNEL_BINDING_TYPES

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

2.7.9版本的新功能。

ssl.OPENSSL_VERSION

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

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

2.7版本的新功能。

ssl.OPENSSL_VERSION_INFO

包含五个整数的元组,代表有关OpenSSL库的版本信息:

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

2.7版本的新功能。

ssl.OPENSSL_VERSION_NUMBER

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

>>> ssl.OPENSSL_VERSION_NUMBER 9470143L >>> hex(ssl.OPENSSL_VERSION_NUMBER) '0x9080bfL'

2.7版本的新功能。

ssl.ALERT_DESCRIPTION_HANDSHAKE_FAILUREssl.ALERT_DESCRIPTION_INTERNAL_ERRORALERT_DESCRIPTION_*

来自RFC 5246和其他的警报描述。该IANA TLS警报注册表包含此列表,并引用到自己的意思被定义RFC文档。

用作回调函数的返回值SSLContext.set_servername_callback()

2.7.9版本的新功能。

Purpose.SERVER_AUTH

选项create_default_context()SSLContext.load_default_certs()。该值表示该上下文可用于对Web服务器进行身份验证(因此,它将用于创建客户端套接字)。

2.7.9版本的新功能。

Purpose.CLIENT_AUTH

选项create_default_context()SSLContext.load_default_certs()。该值表示该上下文可用于对Web客户端进行身份验证(因此,它将用于创建服务器端套接字)。

2.7.9版本的新功能。

2. SSL套接字

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

  • accept()

  • bind()

  • close()

  • connect()

  • fileno()

  • getpeername(), getsockname()

  • getsockopt(), setsockopt()

  • gettimeout(), settimeout(), setblocking()

  • listen()

  • makefile()

  • recv()recv_into()(但flags不允许传递非零参数)

  • send()sendall()(具有相同的限制)

  • shutdown()

但是,由于SSL(和TLS)协议在TCP的顶端有其自己的组帧,SSL套接字抽象在某些方面可能与正常的OS级套接字的规范不同。尤其请参阅关于非阻塞套接字的说明。

SSL套接字还具有以下附加方法和属性:

SSLSocket.do_handshake()

执行SSL设置握手。

在版本2.7.9中更改:match_hostname()check_hostname套接字的属性context为true 时,握手方法也会执行。

SSLSocket.getpeercert(binary_form=False)

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

如果binary_form参数是False,并且从对等端接收到证书,则此方法返回一个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套接字,客户端只会在服务器请求时提供证书; 因此getpeercert()将返回None,如果你使用CERT_NONE(而不是CERT_OPTIONALCERT_REQUIRED)。

在版本2.7.9中更改:返回的字典包括其他项目,如issuernotBeforeValueError当握手没有完成时,提出了另外的建议。返回的字典包括额外采用X509v3扩展项目,如crlDistributionPointscaIssuersOCSP的URI。

SSLSocket.cipher()

返回包含正在使用的密码名称,定义其使用的SSL协议版本以及正在使用的密码位数的三值元组。如果没有建立连接,则返回None

SSLSocket.compression()

返回用作字符串的压缩算法,或者None连接未被压缩。

如果更高级别的协议支持自己的压缩机制,则可以使用OP_NO_COMPRESSION禁用SSL级别的压缩。

2.7.9版本的新功能。

SSLSocket.get_channel_binding(cb_type="tls-unique")

获取当前连接的通道绑定数据,作为字节对象。返回None如果没有连接或握手尚未完成。

所述cb_type参数允许所需的信道绑定类型的选择。CHANNEL_BINDING_TYPES列表中列出了有效的通道绑定类型。目前只支持RFC 5929定义的'tls-unique'通道绑定。ValueError如果请求不支持的通道绑定类型,则会引发此问题。

2.7.9版本的新功能。

SSLSocket.selected_alpn_protocol()

返回在TLS握手期间选择的协议。如果SSLContext.set_alpn_protocols()未被调用,如果另一方不支持ALPN,如果此套接字不支持任何客户端建议的协议,或者握手尚未发生,则返回None。

2.7.10版中的新功能。

SSLSocket.selected_npn_protocol()

返回在TLS / SSL握手期间选择的更高级协议。如果SSLContext.set_npn_protocols()没有被调用,或者对方不支持NPN,或者握手尚未发生,则返回None

2.7.9版本的新功能。

SSLSocket.unwrap()

执行SSL关闭握手,从基础套接字移除TLS层,并返回底层套接字对象。这可以用于从连接上的加密操作转到未加密。返回的套接字应始终用于与连接的另一端进一步通信,而不是原始套接字。

SSLSocket.version()

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

2.7.9版本的新功能。

SSLSocket.context

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

2.7.9版本的新功能。

3. SSL上下文

2.7.9版本的新功能。

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

class ssl.SSLContext(protocol)

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

SSLContext 对象具有以下方法和属性:

SSLContext.cert_store_stats()

获取有关已加载的X.509证书数量的统计信息,以字典形式标记为CA证书和证书撤销列表的X.509证书数。

具有一个CA证书和另一个证书的上下文示例:

>>> context.cert_store_stats() {'crl': 0, 'x509_ca': 1, 'x509': 2}

SSLContext.load_cert_chain(certfile, keyfile=None, password=None)

加载私钥和相应的证书。所述certFile中字符串必须的路径PEM格式包含证书以及任何号码,以建立证书的真实性所需要的CA证书的单个文件。该密钥文件的字符串,如果存在的话,必须指向包含私有密钥的文件,否则私钥将采取certFile中也是如此。见证书的讨论关于证书是如何被存储在信息certFile中

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

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

一个SSLError如果私钥不与证书相匹配上升。

SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH)

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

目的标志指定类型的CA证书加载什么。默认设置Purpose.SERVER_AUTH加载证书,这些证书被标记和信任用于TLS Web服务器身份验证(客户端套接字)。Purpose.CLIENT_AUTH在服务器端加载用于客户端证书验证的CA证书。

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

加载一组“认证权威”一起使用时,以验证其他对等端的证书(CA)证书的verify_mode比其他CERT_NONE。必须至少指定一个cafilecapath

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

凭证档案错误串,如果存在的话,是路径到PEM格式级联CA证书的文件。有关如何在此文件中安排证书的更多信息,请参阅证书的讨论。

capath串,如果存在的话,是路径到包含PEM格式几个CA证书,以下内容的一个目录的OpenSSL特定布局。

cadata对象,如果存在的话,或者是一个或多个PEM编码证书的ASCII字符串或DER编码的证书的对类字节对象。像使用capath一样,PEM编码证书周围的额外行被忽略,但至少必须存在一个证书。

SSLContext.get_ca_certs(binary_form=False)

获取加载的“证书颁发机构”(CA)证书列表。如果binary_form参数是False每个列表项是一个类似输出的字典SSLSocket.getpeercert()。否则,该方法将返回DER编码证书的列表。除非通过SSL连接请求和加载证书,否则返回的列表不包含来自Capath的证书。

注意

在capath目录中的证书不会被加载,除非它们至少被使用过一次。

SSLContext.set_default_verify_paths()

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

SSLContext.set_ciphers(ciphers)

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

注意

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

SSLContext.set_alpn_protocols(protocols)

指定套接字应在SSL / TLS握手期间通告的协议。它应该是一个ASCII字符串列表,比如['http/1.1', 'spdy/2']按照首选项排序。协议的选择将在握手期间进行,并根据RFC 7301进行。在握手成功后,该SSLSocket.selected_alpn_protocol()方法将返回商定的协议。

NotImplementedError如果HAS_ALPN是False,此方法将会引发。

OpenSSL 1.1.0+将中止握手,并SSLError在双方都支持ALPN但不能就协议达成一致时提高。

2.7.10版中的新功能。

SSLContext.set_npn_protocols(protocols)

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

NotImplementedError如果HAS_NPN是False,此方法将会引发。

SSLContext.set_servername_callback(server_name_callback)

注册一个回调函数,当TLS客户端指定服务器名称指示时,SSL / TLS服务器收到TLS客户端Hello握手消息后将调用该函数。服务器名称指示机制在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()方法要求TLS连接已经渐渐超越了TLS客户端问候,并因此将不包含返回有意义的值,也不能被安全地调用。

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

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

如果从server_name_callback函数引发异常,则TLS连接将终止并显示致命的TLS警报消息ALERT_DESCRIPTION_HANDSHAKE_FAILURE

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

SSLContext.load_dh_params(dhfile)

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

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

SSLContext.set_ecdh_curve(curve_name)

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

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

如果HAS_ECDH是,该方法不可用False

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

Wrap an existing Python socket sock and return an SSLSocket object. sock must be a SOCK_STREAM socket; other socket types are unsupported.

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

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

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

SSLContext.session_stats()

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

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

SSLContext.check_hostname

更好地匹配同级证书的主机名与match_hostname()in SSLSocket.do_handshake()。上下文的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))

注意

该功能需要OpenSSL 0.9.8f或更新版本。

SSLContext.options

表示在此上下文中启用的一组SSL选项的整数。默认值是OP_ALL,但您可以指定其他选项,例如OP_NO_SSLv2通过将它们组合在一起。

注意

对于0.9.8m以上版本的OpenSSL,只能设置选项,而不能清除它们。试图清除一个选项(通过重置相应的位)将引发一个ValueError

SSLContext.protocol

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

SSLContext.verify_flags

证书验证操作的标志。您可以VERIFY_CRL_CHECK_LEAF通过将它们组合在一起来设置标志。默认情况下,OpenSSL既不需要也不需要验证证书撤销列表(CRL)。仅适用于openssl 0.9.8+版本。

SSLContext.verify_mode

是否尝试验证其他同行的证书以及验证是否失败时的行为方式。该属性必须是一个CERT_NONECERT_OPTIONALCERT_REQUIRED

4.证书

一般而言,证书是公钥/私钥系统的一部分。在这个系统中,每个委托人(可能是一台机器,一个人或一个组织)被分配一个唯一的两部分加密密钥。密钥的一部分是公共密钥,称为公钥。另一部分保密,称为私钥。这两部分是相关的,因为如果您使用其中一个部分对消息进行加密,则可以使用其他部分对其进行解密,而只能使用其他部分进行解密。

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

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

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

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

4.1。证书链

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

4.2。CA证书

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

4.3。组合密钥和证书

私钥通常与证书存储在同一个文件中; 在这种情况下,只有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-----

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 %

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

5.例子

5.1。测试SSL支持

要测试Python安装中是否存在SSL支持,用户代码应使用以下习惯用法:

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

5.2。客户端操作

本示例使用推荐的客户端套接字安全设置创建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")

(这段代码假设您的操作系统放置了所有CA证书/etc/ssl/certs/ca-bundle.crt;如果没有,您将得到一个错误并且必须调整位置)

当您使用环境连接到服务器时,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'']

请参阅下面关于安全性考虑的讨论。

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.read() # null 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.read() # finished with client

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

6.关于非阻塞套接字的注意事项

使用非阻塞套接字时,需要注意以下几点:

  • 调用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, [])

7.安全考虑

7.1。最好的默认值

对于客户端使用,如果您对安全策略没有任何特殊要求,强烈建议您使用该create_default_context()功能来创建SSL上下文。它将加载系统的可信CA证书,启用证书验证和主机名检查,并尝试选择合理安全的协议和密码设置。

如果连接需要客户端证书,则可以添加该证书SSLContext.load_cert_chain()

相比之下,如果您通过SSLContext自己调用构造函数来创建SSL上下文,则默认情况下将不会启用证书验证或主机名检查。如果您这样做,请阅读下面的段落以达到良好的安全级别。

7.2。手动设置

7.2.1。验证证书

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

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

注意

在客户端模式,CERT_OPTIONAL并且CERT_REQUIRED除非匿名密码被启用(它们被默认禁用)是等价的。

7.2.2。协议版本

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和更高版本(如果您的系统支持)连接。

7.2.3。密码选择

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

7.3。多处理

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