18.2. ssl
— 套接字对象的TLS/SSL封装¶
源代码: Lib/ssl.py
本模块保证了对传安全传输层协议(TLS)的访问 (SSL)加密,并且提供客户端和服务端层面的网络嵌套字层面的对等连接认证技术。本模块使用OpenSSL库。适用于所有现代Unix系统、Windows以及Mac OS X。只要Open SSL存在的系统,都有机会正常使用。
備註
某些行为可能具有平台依赖,因为调用是根据操作系统的嵌套字API。不同版本的Open SSL也会引起差异:例如Open SSL版本1.0.1 自带TLSv1.1 和 TLSv1.2
警告
在阅读 安全考量 前不要使用此模块。 这样做可能会导致虚假的安全感,因为ssl模块的默认设置不一定适合你的应用程序。
本文档记录」ssl」模块的对象和函数;更多关于TLS,SSL,和证书的信息,请参阅下方的“详情”选项
This module provides a class, ssl.SSLSocket
, which is derived from the
socket.socket
type, and provides a socket-like wrapper that also
encrypts and decrypts the data going over the socket with SSL. It supports
additional methods such as getpeercert()
, which retrieves the
certificate of the other side of the connection, and cipher()
,which
retrieves the cipher being used for the secure connection.
对于更复杂的应用程序,ssl.SSLContext
类有助于管理设置项和证书,进而可以被使用 SSLContext.wrap_socket()
方法创建的 SSL 套接字继承。
18.2.1. 方法、常量和异常处理¶
-
exception
ssl.
SSLError
¶ 引发此异常以提示来自下层 SSL 实现(目前由 OpenSSL 库提供)的错误。 它表示在下层网络连接之上叠加的高层级加密和验证层存在某种问题。 此错误是
OSError
的一个子类型。SSLError
实例的错误和消息是由 OpenSSL 库提供的。3.3 版更變:
SSLError
曾经是socket.error
的一个子类型。-
library
¶ 一个字符串形式的助词符,用来指明发生错误的 OpenSSL 子模块,例如
SSL
,PEM
或X509
。 可能的取值范围依赖于 OpenSSL 的版本。3.3 版新加入.
-
reason
¶ 一个字符串形式的助记符,用来指明发生错误的原因,例如
CERTIFICATE_VERIFY_FAILED
。 可能的取值范围依赖于 OpenSSL 的版本。3.3 版新加入.
-
-
exception
ssl.
SSLZeroReturnError
¶ SSLError
的一个子类,当尝试读取或写入且 SSL 连接已被完全关闭时会被引发。 请注意这并不意味着下层的传输(读取 TCP)已被关闭。3.3 版新加入.
-
exception
ssl.
SSLWantReadError
¶ SSLError
的一个子类,当尝试读取或写入但,并在请求被满足之前还需要在下层的 TCP 传输上接收更多数据时会被 非阻塞型 SSL 套接字 引发。3.3 版新加入.
-
exception
ssl.
SSLWantWriteError
¶ SSLError
的一个子类,当尝试读取或写入数据,但在请求被满足之前还需要在下层的 TCP 传输上发送更多数据时会被 非阻塞型 SSL 套接字 引发。3.3 版新加入.
-
exception
ssl.
SSLSyscallError
¶ SSLError
的子类,当尝试在 SSL 套接字上执行操作时遇到系统错误时会被引发。 不幸的是,没有简单的方式能检查原始 errno 编号。3.3 版新加入.
-
exception
ssl.
CertificateError
¶ Raised to signal an error with a certificate (such as mismatching hostname). Certificate errors detected by OpenSSL, though, raise an
SSLError
.
18.2.1.1. 套接字创建¶
The following function allows for standalone socket creation. Starting from
Python 3.2, it can be more flexible to use SSLContext.wrap_socket()
instead.
-
ssl.
wrap_socket
(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None)¶ 接受一个
socket.socket
的实例sock
,并返回一个ssl.SSLSocket
的实例,该类型是socket.socket
的子类型,它将下层的套接字包装在一个 SSL 上下文中。sock
必须是一个SOCK_STREAM
套接字;其他套接字类型不被支持。For client-side sockets, the context construction is lazy; if the underlying socket isn’t connected yet, the context construction will be performed after
connect()
is called on the socket. For server-side sockets, if the socket has no remote peer, it is assumed to be a listening socket, and the server-side SSL wrapping is automatically performed on client connections accepted via theaccept()
method.wrap_socket()
may raiseSSLError
.The
keyfile
andcertfile
parameters specify optional files which contain a certificate to be used to identify the local side of the connection. See the discussion of 证书 for more information on how the certificate is stored in thecertfile
.形参
server_side
是一个布尔值,它标明希望从该套接字获得服务器端行为还是客户端行为。The parameter
cert_reqs
specifies whether a certificate is required from the other side of the connection, and whether it will be validated if provided. It must be one of the three valuesCERT_NONE
(certificates ignored),CERT_OPTIONAL
(not required, but validated if provided), orCERT_REQUIRED
(required and validated). If the value of this parameter is notCERT_NONE
, then theca_certs
parameter must point to a file of CA certificates.The
ca_certs
file contains a set of concatenated 「certification authority」 certificates, which are used to validate certificates passed from the other end of the connection. See the discussion of 证书 for more information about how to arrange the certificates in this file.The parameter
ssl_version
specifies which version of the SSL protocol to use. Typically, the server chooses a particular protocol version, and the client must adapt to the server’s choice. Most of the versions are not interoperable with the other versions. If not specified, the default isPROTOCOL_TLS
; it provides the most compatibility with other versions.这个表显示了客户端(横向)的哪个版本能够连接服务器(纵向)的哪个版本。
client / server SSLv2 SSLv3 TLS TLSv1 TLSv1.1 TLSv1.2 SSLv2 是 否 是 否 否 否 SSLv3 否 是 是 否 否 否 TLS (SSLv23) 否 是 是 是 是 是 TLSv1 否 否 是 是 否 否 TLSv1.1 否 否 是 否 是 否 TLSv1.2 否 否 是 否 否 是 備註
Which connections succeed will vary depending on the version of OpenSSL. For example, before OpenSSL 1.0.0, an SSLv23 client would always attempt SSLv2 connections.
The ciphers parameter sets the available ciphers for this SSL object. It should be a string in the OpenSSL cipher list format.
形参
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 版更變: New optional argument ciphers.
18.2.1.2. 上下文创建¶
便捷函数,可以帮助创建 SSLContext
对象,用于常见的目的。
-
ssl.
create_default_context
(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)¶ 返回一个新的
SSLContext
对象,使用给定 purpose 的默认设置。 该设置由ssl
模块选择,并且通常是代表一个比直接调用SSLContext
构造器时更高的安全等级。cafile, capath, cadata 代表用于进行证书核验的可选受信任 CA 证书,与
SSLContext.load_verify_locations()
的一致。 如果三个参数均为None
,此函数可以转而选择信任系统的默认 CA 证书。设置包括:
PROTOCOL_TLS
,OP_NO_SSLv2
以及OP_NO_SSLv3
,具有不带 RC4 和不带无身份验证密码套件的高度加密密码套件。 传入SERVER_AUTH
作为 purpose 会把verify_mode
设为CERT_REQUIRED
并且加载指定 CA 证书(当给出 cafile, capath 和 cadata 中的至少一个)或者使用SSLContext.load_default_certs()
来加载默认 CA 证书。備註
协议、选项、密码和其他设置可随时更改为更具约束性的值而无须事先弃用。 这些值代表了兼容性和安全性之间的合理平衡。
如果你的应用需要特定的设置,你应当创建一个
SSLContext
并自行应用设置。備註
如果你发现当某些较旧的客户端或服务器尝试与用此函数创建的
SSLContext
进行连接时收到了报错提示 「Protocol or cipher suite mismatch」,这可能是因为它们只支持 SSL3.0 而它被此函数用OP_NO_SSLv3
排除掉了。 SSL3.0 被广泛认为 完全不可用。 如果你仍希望继续使用此函数但仍允许 SSL 3.0 连接,你可以使用以下代码重新启用它们:ctx = ssl.create_default_context(Purpose.CLIENT_AUTH) ctx.options &= ~ssl.OP_NO_SSLv3
3.4 版新加入.
3.4.4 版更變: RC4 被从默认密码字符串中丢弃。
3.5.3 版更變: ChaCha20/Poly1305 被添加到默认密码字符串中。
3DES 被从默认密码字符串中丢弃。
18.2.1.3. 随机生成¶
-
ssl.
RAND_bytes
(num)¶ 返回 num 个高加密强度伪随机字节数据。 如果 PRNG 未使用足够的数据作为随机种子或者如果当前 RAND 方法不支持该操作则会引发
SSLError
。RAND_status()
可被用来检查 PRNG 的状态而RAND_add()
可被用来为 PRNG 设置随机种子。对于几乎所有应用程序都更推荐使用
os.urandom()
。Read the Wikipedia article, Cryptographically secure pseudorandom number generator (CSPRNG), to get the requirements of a cryptographically generator.
3.3 版新加入.
-
ssl.
RAND_pseudo_bytes
(num)¶ 返回 (bytes, is_cryptographic): bytes 是 num 个伪随机字节数据,如果所生成的字节数据为高加密强度则 is_cryptographic 为
True
。 如果当前 RAND 方法不支持此操作则会引发SSLError
。所生成的伪随机字节序列如果具有足够的长度则将会具有唯一性,并是并非不可预测。 它们可被用于非加密目的以及加密协议中的特定目的,但通常不可被用于密钥生成等目的。
对于几乎所有应用程序都更推荐使用
os.urandom()
。3.3 版新加入.
3.5.3 版後已棄用: OpenSSL 已弃用了
ssl.RAND_pseudo_bytes()
,请改用ssl.RAND_bytes()
。
-
ssl.
RAND_status
()¶ 如果 SSL 伪随机数生成器已使用‘足够的’随机性作为种子则返回
True
,否则返回False
。 你可以使用ssl.RAND_egd()
和ssl.RAND_add()
来增加伪随机数生成器的随机性。
-
ssl.
RAND_egd
(path)¶ 如果你在某处运行了一个熵收集守护程序(EGD),且 path 是向其打开的套接字连接路径名,此函数将从该套接字读取 256 个字节的随机性数据,并将其添加到 SSL 伪随机数生成器以增加所生成密钥的安全性。 此操作通常只在没有更好随机性源的系统上才是必要的。
请查看 http://egd.sourceforge.net/ 或 http://prngd.sourceforge.net/ 来了解有关熵收集守护程序源的信息。
Availability: not available with LibreSSL and OpenSSL > 1.1.0
18.2.1.4. 证书处理¶
-
ssl.
match_hostname
(cert, hostname)¶ Verify that cert (in decoded format as returned by
SSLSocket.getpeercert()
) matches the given hostname. The rules applied are those for checking the identity of HTTPS servers as outlined in RFC 2818 and RFC 6125. In addition to HTTPS, this function should be suitable for checking the identity of servers in various SSL-based protocols such as FTPS, IMAPS, POPS and others.失败时引发
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 版更變: 现在支持匹配存在于证书的 subjectAltName 字段中的 IP 地址。
-
ssl.
cert_time_to_seconds
(cert_time)¶ 返回距离 Unix 纪元零时的秒数,给定的
cert_time
字符串代表来自证书的 「notBefore」 或 「notAfter」 日期值,采用"%b %d %H:%M:%S %Y %Z"
strptime 格式(C 区域)。以下为示例代码:
>>> 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 版更變: 将输入时间解读为 UTC 时间,基于输入字符串中指明的 『GMT』 时区。 在之前使用的是本地时区。 返回一个整数(不带输入格式中秒的分数部分)
-
ssl.
get_server_certificate
(addr, ssl_version=PROTOCOL_TLS, ca_certs=None)¶ Given the address
addr
of an SSL-protected server, as a (hostname, port-number) pair, fetches the server’s certificate, and returns it as a PEM-encoded string. Ifssl_version
is specified, uses that version of the SSL protocol to attempt to connect to the server. Ifca_certs
is specified, it should be a file containing a list of root certificates, the same format as used for the same parameter inwrap_socket()
. The call will attempt to validate the server certificate against that set of root certificates, and will fail if the validation attempt fails.3.3 版更變: 此函数现在是 IPv6 兼容的。-compatible.
3.5 版更變: 默认的 ssl_version 从
PROTOCOL_SSLv3
改为PROTOCOL_TLS
以保证与现代服务器的最大兼容性。
-
ssl.
DER_cert_to_PEM_cert
(DER_cert_bytes)¶ 根据给定的 DER 编码字节块形式的证书,返回同一证书的 PEM 编码字符串版本。
-
ssl.
PEM_cert_to_DER_cert
(PEM_cert_string)¶ 根据给定的 ASCII PEM 字符串形式的证书,返回同一证书的 DER 编码字节序列。
-
ssl.
get_default_verify_paths
()¶ 返回包含 OpenSSL 的默认 cafile 和 capath 的路径的命名元组。 此路径与
SSLContext.set_default_verify_paths()
所使用的相同。 返回值是一个 named tupleDefaultVerifyPaths
:cafile
- 解析出的 cafile 路径或者如果文件不存在则为None
,capath
- 解析出的 capath 路径或者如果目录不存在则为None
,openssl_cafile_env
- 指向一个 cafile 的 OpenSSL 环境键,openssl_cafile
- 一个 cafile 的硬编码路径,openssl_capath_env
- 指向一个 capath 的 OpenSSL 环境键,openssl_capath
- 一个 capath 目录的硬编码路径
Availability: LibreSSL ignores the environment vars
openssl_cafile_env
andopenssl_capath_env
3.4 版新加入.
-
ssl.
enum_certificates
(store_name)¶ 从 Windows 的系统证书库中检索证书。 store_name 可以是
CA
,ROOT
或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)]
Availability: 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 的编码格式。 它可以为
x509_asn
以表示 X.509 ASN.1 数据或是pkcs_7_asn
以表示 PKCS#7 ASN.1 数据。Availability: Windows.
3.4 版新加入.
18.2.1.5. 常量¶
-
ssl.
CERT_NONE
¶ Possible value for
SSLContext.verify_mode
, or thecert_reqs
parameter towrap_socket()
. In this mode (the default), no certificates will be required from the other side of the socket connection. If a certificate is received from the other end, no attempt to validate it is made.参见下文对于 安全考量 的讨论。
-
ssl.
CERT_OPTIONAL
¶ Possible value for
SSLContext.verify_mode
, or thecert_reqs
parameter towrap_socket()
. In this mode no certificates will be required from the other side of the socket connection; but if they are provided, validation will be attempted and anSSLError
will be raised on failure.使用此设置要求将一组有效的 CA 证书传递给
SSLContext.load_verify_locations()
或是作为wrap_socket()
的ca_certs
形参值。
-
ssl.
CERT_REQUIRED
¶ Possible value for
SSLContext.verify_mode
, or thecert_reqs
parameter towrap_socket()
. In this mode, certificates are required from the other side of the socket connection; anSSLError
will be raised if no certificate is provided, or if its validation fails.使用此设置要求将一组有效的 CA 证书传递给
SSLContext.load_verify_locations()
或是作为wrap_socket()
的ca_certs
形参值。
-
ssl.
VERIFY_DEFAULT
¶ SSLContext.verify_flags
可能的取值。 在此模式下,证书吊销列表(CRL)并不会被检查。 OpenSSL 默认不要求也不验证 CRL。3.4 版新加入.
-
ssl.
VERIFY_CRL_CHECK_LEAF
¶ Possible value for
SSLContext.verify_flags
. In this mode, only the peer cert is check but non of the intermediate CA certificates. The mode requires a valid CRL that is signed by the peer cert’s issuer (its direct ancestor CA). If no proper has been loadedSSLContext.load_verify_locations
, validation will fail.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_TLS
¶ Selects the highest protocol version that both the client and server support. Despite the name, this option can select 「TLS」 protocols as well as 「SSL」.
3.5.3 版新加入.
-
ssl.
PROTOCOL_SSLv23
¶ Alias for data:PROTOCOL_TLS.
3.5.3 版後已棄用: Use data:PROTOCOL_TLS instead.
-
ssl.
PROTOCOL_SSLv2
¶ 选择 SSL 版本 2 作为通道加密协议。
如果 OpenSSL 编译时附带了
OPENSSL_NO_SSL2
旗标则此协议将不可用。警告
SSL 版本 2 并不安全。 极不建议使用它。
3.5.3 版後已棄用: OpenSSL 已经移除了对 SSLv2 的支持。
-
ssl.
PROTOCOL_SSLv3
¶ 选择 SSL 版本 3 作为通道加密协议。
如果 OpenSSL 编译时使用了
OPENSSL_NO_SSLv3
旗标则此协议将不可用。警告
SSL 版本 3 并不安全。 极不建议使用它。
3.5.3 版後已棄用: OpenSSL has deprecated all version specific protocols. Use the default protocol data:PROTOCOL_TLS with flags like data:OP_NO_SSLv3 instead.
-
ssl.
PROTOCOL_TLSv1
¶ 选择 TLS 版本 1.0 作为通道加密协议。
3.5.3 版後已棄用: OpenSSL has deprecated all version specific protocols. Use the default protocol data:PROTOCOL_TLS with flags like data:OP_NO_SSLv3 instead.
-
ssl.
PROTOCOL_TLSv1_1
¶ 选择 TLS 版本 1.1 作为通道加密协议。 仅适用于 openssl 版本 1.0.1+。
3.4 版新加入.
3.5.3 版後已棄用: OpenSSL has deprecated all version specific protocols. Use the default protocol data:PROTOCOL_TLS with flags like data:OP_NO_SSLv3 instead.
-
ssl.
PROTOCOL_TLSv1_2
¶ 选译 TLS 版本 1.2 作为通道加密协议。 这是最新的版本,也应是能提供最大保护的最佳选择,如果通信双方都支持它的话。 仅适用于 openssl 版本 1.0.1+。
3.4 版新加入.
3.5.3 版後已棄用: OpenSSL has deprecated all version specific protocols. Use the default protocol data:PROTOCOL_TLS with flags like data:OP_NO_SSLv3 instead.
-
ssl.
OP_ALL
¶ 对存在于其他 SSL 实现中的各种缺陷启用绕过操作。 默认会设置此选项。 没有必要设置与 OpenSSL 的
SSL_OP_ALL
常量同名的旗标。3.2 版新加入.
-
ssl.
OP_NO_SSLv2
¶ 阻止 SSLv2 连接。 此选项仅可与
PROTOCOL_TLS
结合使用。 它会阻止对等方选择 SSLv2 作为协议版本。3.2 版新加入.
3.5.3 版後已棄用: SSLv2 已被弃用。
-
ssl.
OP_NO_SSLv3
¶ 阻止 SSLv3 连接。 此选项仅可与
PROTOCOL_TLS
结合使用。 它会阻止对等方选择 SSLv3 作为协议版本。3.2 版新加入.
3.5.3 版後已棄用: SSLv3 已被弃用
-
ssl.
OP_NO_TLSv1
¶ 阻止 TLSv1 连接。 此选项仅可与
PROTOCOL_TLS
结合使用。 它会阻止对等方选择 TLSv1 作为协议版本。3.2 版新加入.
-
ssl.
OP_NO_TLSv1_1
¶ 阻止 TLSv1.1 连接。 此选项仅可与
PROTOCOL_TLS
结合使用。 它会阻止对等方选择 TLSv1.1 作为协议版本。 仅适用于 openssl 版本 1.0.1+。3.4 版新加入.
-
ssl.
OP_NO_TLSv1_2
¶ 阻止 TLSv1.2 连接。 此选项仅可与
PROTOCOL_TLS
结合使用。 它会阻止对等方选择 TLSv1.2 作为协议版本。 仅适用于 openssl 版本 1.0.1+。3.4 版新加入.
-
ssl.
OP_CIPHER_SERVER_PREFERENCE
¶ 使用服务器的密码顺序首选项,而不是客户端的首选项。 此选项在客户端套接字和 SSLv2 服务器套接字上无效。
3.3 版新加入.
-
ssl.
OP_SINGLE_DH_USE
¶ 阻止对于单独的 SSL 会话重用相同的 DH 密钥。 这会提升前向保密性但需要更多的计算资源。 此选项仅适用于服务器套接字。
3.3 版新加入.
-
ssl.
OP_SINGLE_ECDH_USE
¶ 阻止对于单独的 SSL 会话重用相同的 ECDH 密钥。 这会提升前向保密性但需要更多的计算资源。 此选项仅适用于服务器套接字。
3.3 版新加入.
-
ssl.
OP_NO_COMPRESSION
¶ 在 SSL 通道上禁用压缩。 这适用于应用协议支持自己的压缩方案的情况。
此选项仅适用于 OpenSSL 1.0.0 及更新的版本。
3.3 版新加入.
-
ssl.
HAS_ECDH
¶ Whether the OpenSSL library has built-in support for Elliptic Curve-based Diffie-Hellman key exchange. This should be true unless the feature was explicitly disabled by the distributor.
3.3 版新加入.
-
ssl.
HAS_SNI
¶ Whether the OpenSSL library has built-in support for the Server Name Indication extension (as defined in RFC 4366).
3.2 版新加入.
-
ssl.
HAS_NPN
¶ Whether the OpenSSL library has built-in support for Next Protocol Negotiation as described in the NPN draft specification. When true, you can use the
SSLContext.set_npn_protocols()
method to advertise which protocols you want to support.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 Alert Registry 中包含了这个列表及对定义其含义的 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 套接字¶
-
class
ssl.
SSLSocket
(socket.socket)¶ SSL 套接字提供了 套接字对象 的下列方法:
accept()
bind()
close()
connect()
detach()
fileno()
getpeername()
,getsockname()
getsockopt()
,setsockopt()
gettimeout()
,settimeout()
,setblocking()
listen()
makefile()
recv()
,recv_into()
(but passing a non-zeroflags
argument is not allowed)send()
,sendall()
(with the same limitation)sendfile()
(butos.sendfile
will be used for plain-text sockets only, elsesend()
will be used)shutdown()
但是,由于 SSL(和 TLS)协议在 TCP 之上具有自己的框架,因此 SSL 套接字抽象在某些方面可能与常规的 OS 层级套接字存在差异。 特别是要查看 非阻塞型套接字说明。
Usually,
SSLSocket
are not created directly, but using thewrap_socket()
function or theSSLContext.wrap_socket()
method.3.5 版更變: 新增了
sendfile()
方法。3.5 版更變:
shutdown()
不会在每次接收或发送字节数据后重置套接字超时。 现在套接字超时为关闭的最大总持续时间。
SSL 套接字还具有下列方法和属性:
-
SSLSocket.
read
(len=1024, buffer=None)¶ 从 SSL 套接字读取至多 len 个字节的数据并将结果作为
bytes
实例返回。 如果指定了 buffer,则改为读取到缓冲区,并返回所读取的字节数。如果套接字为 非阻塞型 则会引发
SSLWantReadError
或SSLWantWriteError
且读取将阻塞。由于在任何时候重新协商都是可能的,因此调用
read()
也可能导致写入操作。3.5 版更變: 套接字超时在每次接收或发送字节数据后不会再被重置。 现在套接字超时为读取至多 len 个字节数据的最大总持续时间。
-
SSLSocket.
write
(buf)¶ 将 buf 写入到 SSL 套接字并返回所写入的字节数。 buf 参数必须为支持缓冲区接口的对象。
如果套接字为 非阻塞型 则会引发
SSLWantReadError
或SSLWantWriteError
且读取将阻塞。由于在任何时候重新协商都是可能的,因此调用
write()
也可能导致读取操作。3.5 版更變: 套接字超时在每次接收或发送字节数据后不会再被重置。 现在套接字超时为写入 buf 的最大总持续时间。
備註
read()
和 write()
方法是读写未加密的应用级数据,并将其解密/加密为带加密的线路级数据的低层级方法。 这些方法需要有激活的 SSL 连接,即握手已完成而 SSLSocket.unwrap()
尚未被调用。
-
SSLSocket.
do_handshake
()¶ 执行 SSL 设置握手。
3.4 版更變: 当套接字的
context
的check_hostname
属性为真值时此握手方法还会执行match_hostname()
。3.5 版更變: 套接字超时在每次接收或发送字节数据时不会再被重置。 现在套接字超时为握手的最大总持续时间。
-
SSLSocket.
getpeercert
(binary_form=False)¶ 如果连接另一端的对等方没有证书,则返回
None
。 如果 SSL 握手还未完成,则会引发ValueError
。如果
binary_form
形参为False
,并且从对等方接收到了证书,此方法将返回一个dict
实例。 如果证书未通过验证,则字典将为空。 如果证书通过验证,它将返回由多个密钥组成的字典,其中包括subject
(证书颁发给的主体) 和issuer
(颁发证书的主体)。 如果证书包含一个 Subject Alternative Name 扩展的实例 (see RFC 3280),则字典中还将有一个subjectAltName
键。subject
和issuer
字段都是包含在证书中相应字段的数据结构中给出的相对专有名称(RDN)序列的元组,每个 RDN 均为 name-value 对的序列。 这里是一个实际的示例:{'issuer': ((('countryName', 'IL'),), (('organizationName', 'StartCom Ltd.'),), (('organizationalUnitName', 'Secure Digital Certificate Signing'),), (('commonName', 'StartCom Class 2 Primary Intermediate Server CA'),)), 'notAfter': 'Nov 22 08:15:19 2013 GMT', 'notBefore': 'Nov 21 03:09:52 2011 GMT', 'serialNumber': '95F0', 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),), (('countryName', 'US'),), (('stateOrProvinceName', 'California'),), (('localityName', 'San Francisco'),), (('organizationName', 'Electronic Frontier Foundation, Inc.'),), (('commonName', '*.eff.org'),), (('emailAddress', 'hostmaster@eff.org'),)), 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')), 'version': 3}
備註
要验证特定服务的证书,你可以使用
match_hostname()
函数。如果
binary_form
形参为True
,并且提供了证书,此方法会将整个证书的 DER 编码形式作为字节序列返回,或者如果对等方未提供证书则返回None
。 对等方是否提供证书取决于 SSL 套接字的角色:- 对于客户端 SSL 套接字,服务器将总是提供证书,无论是否需要进行验证;
- 对于服务器 SSL 套接字,客户端将仅在服务器要求时才提供证书;因此如果你使用了
CERT_NONE
(而不是CERT_OPTIONAL
或CERT_REQUIRED
) 则getpeercert()
将返回None
。
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
()¶ 返回在Return the higher-level protocol that was selected during the 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
¶ The
SSLContext
object this SSL socket is tied to. If the SSL socket was created using the top-levelwrap_socket()
function (rather thanSSLContext.wrap_socket()
), this is a custom context object created for this SSL socket.3.2 版新加入.
-
SSLSocket.
server_side
¶ 一个布尔值,对于服务器端套接字为
True
而对于客户端套接字则为False
。3.2 版新加入.
18.2.3. SSL 上下文¶
3.2 版新加入.
SSL 上下文可保存各种比单独 SSL 连接寿命更长的数据,例如 SSL 配置选项,证书和私钥等。 它还可为服务器端套接字管理缓存,以加快来自相同客户端的重复连接。
-
class
ssl.
SSLContext
(protocol=PROTOCOL_TLS)¶ Create a new SSL context. You may pass protocol which must be one of the
PROTOCOL_*
constants defined in this module.PROTOCOL_TLS
is currently recommended for maximum interoperability and default value.也參考
create_default_context()
让ssl
为特定目标选择安全设置。3.5.3 版更變:
PROTOCOL_TLS
is the default value.
SSLContext
对象具有以下方法和属性:
-
SSLContext.
cert_store_stats
()¶ 获取以字典表示的有关已加载的 X.509 证书数量,被标记为 CA 证书的 X.509 证书数量以及证书吊销列表的的统计信息。
具有一个 CA 证书和一个其他证书的上下文示例:
>>> context.cert_store_stats() {'crl': 0, 'x509_ca': 1, 'x509': 2}
3.4 版新加入.
-
SSLContext.
load_cert_chain
(certfile, keyfile=None, password=None)¶ 加载一个私钥及对应的证书。 certfile 字符串必须为以 PEM 格式表示的单个文件路径,该文件中包含证书以及确立证书真实性所需的任意数量的 CA 证书。 如果存在 keyfile 字符串,它必须指向一个包含私钥的文件。 否则私钥也将从 certfile 中提取。 请参阅 证书 中的讨论来了解有关如何将证书存储至 certfile 的更多信息。
password 参数可以是一个函数,调用时将得到用于解密私钥的密码。 它在私钥被加密且需要密码时才会被调用。 它调用时将不带任何参数,并且应当返回一个字符串、字节串或字节数组。 如果返回值是一个字符串,在用它解密私钥之前它将以 UTF-8 进行编码。 或者也可以直接将字符串、字节串或字节数组值作为 password 参数提供。 如果私钥未被加密且不需要密码则它将被忽略。
如果未指定 password 参数且需要一个密码,将会使用 OpenSSL 内置的密码提示机制来交互式地提示用户输入密码。
如果私钥不能匹配证书则会引发
SSLError
。3.3 版更變: 新增可选参数 password。
-
SSLContext.
load_default_certs
(purpose=Purpose.SERVER_AUTH)¶ 从默认位置加载一组默认的 「证书颁发机构」 (CA) 证书。 在 Windows 上它将从
CA
和ROOT
系统存储中加载 CA 证书。 在其他系统上它会调用SSLContext.set_default_verify_paths()
。 将来该方法也可能会从其他位置加载 CA 证书。purpose 旗标指明要加载哪一类 CA 证书。 默认设置
Purpose.SERVER_AUTH
加载被标记且被信任用于 TLS Web 服务器验证(客户端套接字)的证书。Purpose.CLIENT_AUTH
则加载用于在服务器端进行客户端证书验证的 CA 证书。3.4 版新加入.
-
SSLContext.
load_verify_locations
(cafile=None, capath=None, cadata=None)¶ 当
verify_mode
不为CERT_NONE
时加载一组用于验证其他对等方证书的 「证书颁发机构」 (CA) 证书。 必须至少指定 cafile 或 capath 中的一个。此方法还可加载 PEM 或 DER 格式的证书吊销列表 (CRL),为此必须正确配置
SSLContext.verify_flags
。如果存在 cafile 字符串,它应为 PEM 格式的级联 CA 证书文件的路径。 请参阅 证书 中的讨论来了解有关如何处理此文件中的证书的更多信息。
The capath string, if present, is the path to a directory containing several CA certificates in PEM format, following an OpenSSL specific layout.
如果存在 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()
输出的字典。 在其他情况下此方法将返回一个 DER 编码的证书的列表。 返回的列表不包含来自 capath 的证书,除非 SSL 连接请求并加载了一个证书。備註
capath 目录中的证书不会被加载,除非它们已至少被使用过一次。
3.4 版新加入.
-
SSLContext.
set_default_verify_paths
()¶ 从构建 OpenSSL 库时定义的文件系统路径中加载一组默认的 「证书颁发机构」 (CA) 证书。 不幸的是,没有一种简单的方式能知道此方法是否执行成功:如果未找到任何证书也不会返回错误。 不过,当 OpenSSL 库是作为操作系统的一部分被提供时,它的配置应当是正确的。
-
SSLContext.
set_ciphers
(ciphers)¶ Set the available ciphers for sockets created with this context. It should be a string in the OpenSSL cipher list format. If no cipher can be selected (because compile-time options or other configuration forbids use of all the specified ciphers), an
SSLError
will be raised.備註
在连接后,SSL 套接字的
SSLSocket.cipher()
方法将给出当前所选择的密码。
-
SSLContext.
set_alpn_protocols
(protocols)¶ 指定在 SSL/TLS 握手期间套接字应当通告的协议。 它应为由 ASCII 字符串组成的列表,例如
['http/1.1', 'spdy/2']
,按首选顺序排列。 协议的选择将在握手期间发生,并依据 RFC 7301 来执行。 在握手成功后,SSLSocket.selected_alpn_protocol()
方法将返回已达成一致的协议。This method will raise
NotImplementedError
ifHAS_ALPN
is False.OpenSSL 1.1.0+ will abort the handshake and raise
SSLError
when both sides support ALPN but cannot agree on a protocol.3.5 版新加入.
-
SSLContext.
set_npn_protocols
(protocols)¶ Specify which protocols the socket should advertise during the SSL/TLS handshake. It should be a list of strings, like
['http/1.1', 'spdy/2']
, ordered by preference. The selection of a protocol will happen during the handshake, and will play out according to the NPN draft specification. After a successful handshake, theSSLSocket.selected_npn_protocol()
method will return the agreed-upon protocol.This method will raise
NotImplementedError
ifHAS_NPN
is False.3.3 版新加入.
-
SSLContext.
set_servername_callback
(server_name_callback)¶ 注册一个回调函数,当 TLS 客户端指定了一个服务器名称提示时,该回调函数将在 SSL/TLS 服务器接收到 TLS Client Hello 握手消息后被调用。 服务器名称提示机制的定义见 RFC 6066 section 3 - Server Name Indication。
Only one callback can be set per
SSLContext
. If server_name_callback isNone
then the callback is disabled. Calling this function a subsequent time will disable the previously registered callback.The callback function, server_name_callback, will be called with three arguments; the first being the
ssl.SSLSocket
, the second is a string that represents the server name that the client is intending to communicate (orNone
if the TLS Client Hello does not contain a server name) and the third argument is the originalSSLContext
. The server name argument is the IDNA decoded server name.此回调的一个典型用法是将
ssl.SSLSocket
的SSLSocket.context
属性修改为一个SSLContext
类型的新对象,该对象代表与服务器相匹配的证书链。由于 TLS 连接处于早期协商阶段,因此仅能使用有限的方法和属性例如
SSLSocket.selected_alpn_protocol()
和SSLSocket.context
。SSLSocket.getpeercert()
,SSLSocket.getpeercert()
,SSLSocket.cipher()
和SSLSocket.compress()
方法要求 TLS 连接已经过 TLS Client Hello 因而将既不包含返回有意义的值,也不能安全地调用它们。The server_name_callback function must return
None
to allow the TLS negotiation to continue. If a TLS failure is required, a constantALERT_DESCRIPTION_*
can be returned. Other return values will result in a TLS fatal error withALERT_DESCRIPTION_INTERNAL_ERROR
.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.If an exception is raised from the server_name_callback function the TLS connection will terminate with a fatal TLS alert message
ALERT_DESCRIPTION_HANDSHAKE_FAILURE
.如果 OpenSSL library 库在构建时定义了 OPENSSL_NO_TLSEXT 则此方法将返回
NotImplementedError
。3.4 版新加入.
-
SSLContext.
load_dh_params
(dhfile)¶ Load the key generation parameters for Diffie-Helman (DH) key exchange. Using DH key exchange improves forward secrecy at the expense of computational resources (both on the server and on the client). The dhfile parameter should be the path to a file containing DH parameters in PEM format.
此设置不会应用于客户端套接字。 你还可以使用
OP_SINGLE_DH_USE
选项来进一步提升安全性。3.3 版新加入.
-
SSLContext.
set_ecdh_curve
(curve_name)¶ 为基于椭圆曲线的 Elliptic Curve-based Diffie-Hellman (ECDH) 密钥交换设置曲线名称。 ECDH 显著快于常规 DH 同时据信同样安全。 curve_name 形参应为描述某个知名椭圆曲线的字符串,例如受到广泛支持的曲线
prime256v1
。此设置不会应用于客户端套接字。 你还可以使用
OP_SINGLE_ECDH_USE
选项来进一步提升安全性。如果
HAS_ECDH
为False
则此方法将不可用。3.3 版新加入.
也參考
- SSL/TLS & Perfect Forward Secrecy
- Vincent Bernat。
-
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 aSOCK_STREAM
socket; other socket types are unsupported.The returned SSL socket is tied to the context, its settings and certificates. The parameters server_side, do_handshake_on_connect and suppress_ragged_eofs have the same meaning as in the top-level
wrap_socket()
function.在客户端连接上,可选形参 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)¶ Create a new
SSLObject
instance by wrapping the BIO objects incoming and outgoing. The SSL routines will read input data from the incoming BIO and write data to the outgoing BIO.The server_side and server_hostname parameters have the same meaning as in
SSLContext.wrap_socket()
.
-
SSLContext.
session_stats
()¶ Get statistics about the SSL sessions created or managed by this context. A dictionary is returned which maps the names of each piece of information to their numeric values. For example, here is the total number of hits and misses in the session cache since the context was created:
>>> stats = context.session_stats() >>> stats['hits'], stats['misses'] (0, 0)
-
SSLContext.
check_hostname
¶ Whether to match the peer cert’s hostname with
match_hostname()
inSSLSocket.do_handshake()
. The context’sverify_mode
must be set toCERT_OPTIONAL
orCERT_REQUIRED
, and you must pass server_hostname towrap_socket()
in order to match the hostname.示例:
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
,但你也可以通过在选项间进行 OR 运算来指定其他选项例如OP_NO_SSLv2
。備註
With versions of OpenSSL older than 0.9.8m, it is only possible to set options, not to clear them. Attempting to clear an option (by resetting the corresponding bits) will raise a
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. 证书¶
总的来说证书是公钥/私钥系统的一个组成部分。 在这个系统中,每 个 主体 (可能是一台机器、一个人或者一个组织) 都会分配到唯一的包含两部分的加密密钥。 一部分密钥是公开的,称为 公钥;另一部分密钥是保密的,称为 私钥。 这两个部分是互相关联的,就是说如果你用其中一个部分来加密一条消息,你将能用并且 只能 用另一个部分来解密它。
A certificate contains information about two principals. It contains the name of a subject, and the subject’s public key. It also contains a statement by a second principal, the issuer, that the subject is who he claims to be, and that this is indeed the subject’s public key. The issuer’s statement is signed with the issuer’s private key, which only the issuer knows. However, anyone can verify the issuer’s statement by finding the issuer’s public key, decrypting the statement with it, and comparing it to the other information in the certificate. The certificate also contains information about the time period over which it is valid. This is expressed as two fields, called 「notBefore」 and 「notAfter」.
在 Python 中应用证书时,客户端或服务器可以用证书来证明自己的身份。 还可以要求网络连接的另一方提供证书,提供的证书可以用于验证以满足客户端或服务器的验证要求。 如果验证失败,连接尝试可被设置为引发一个异常。 验证是由下层的 OpenSSL 框架来自动执行的;应用程序本身不必关注其内部的机制。 但是应用程序通常需要提供一组证书以允许此过程的发生。
Python 使用文件来包含证书。 它们应当采用 「PEM」 格式 (参见 RFC 1422),这是一种带有头部行和尾部行的 base-64 编码包装形式:
-----BEGIN CERTIFICATE-----
... (certificate in base64 PEM encoding) ...
-----END CERTIFICATE-----
18.2.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-----
18.2.4.2. CA 证书¶
如果你想要求对连接的另一方的证书进行验证,你必须提供一个 「CA 证书」 文件,其中包含了你愿意信任的每个颁发方的证书链。 同样地,这个文件的内容就是这些证书链拼接在一起的结果。 为了进行验证,Python 将使用它在文件中找到的第一个匹配的证书链。 可以通过调用 SSLContext.load_default_certs()
来使用系统平台的证书文件,这可以由 create_default_context()
自动完成。
18.2.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-----
18.2.4.4. 自签名证书¶
如果你准备创建一个提供 SSL 加密连接服务的服务器,你需要为该服务获取一份证书。 有许多方式可以获取合适的证书,例如从证书颁发机构购买。 另一种常见做法是生成自签名证书。 生成自签名证书的最简单方式是使用 OpenSSL 软件包,代码如下所示:
% openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem
Generating a 1024 bit RSA private key
.......++++++
.............................++++++
writing new private key to 'cert.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:MyState
Locality Name (eg, city) []:Some City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc.
Organizational Unit Name (eg, section) []:My Group
Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com
Email Address []:ops@myserver.mygroup.myorganization.com
%
自签名证书的缺点在于它是它自身的根证书,因此不会存在于别人的已知(且信任的)根证书缓存当中。
18.2.5. 例子¶
18.2.5.1. 检测 SSL 支持¶
要检测一个 Python 安装版中是否带有 SSL 支持,用户代码应当使用以下例程:
try:
import ssl
except ImportError:
pass
else:
... # do something that requires SSL support
18.2.5.2. 客户端操作¶
这个例子创建了一个 SSL 上下文并使用客户端套接字的推荐安全设置,包括自动证书验证:
>>> context = ssl.create_default_context()
如果你喜欢自行调整安全设置,你可能需要从头创建一个上下文(但是请请注意避免不正确的设置):
>>> context = ssl.SSLContext(ssl.PROTOCOL_TLS)
>>> context.verify_mode = ssl.CERT_REQUIRED
>>> context.check_hostname = True
>>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt")
(这段代码假定你的操作系统将所有 CA 证书打包存放于 /etc/ssl/certs/ca-bundle.crt
;如果不是这样,你将收到报错信息,必须修改此位置)
When you use the context to connect to a server, CERT_REQUIRED
validates the server certificate: it ensures that the server certificate
was signed with one of the CA certificates, and checks the signature for
correctness:
>>> conn = context.wrap_socket(socket.socket(socket.AF_INET),
... server_hostname="www.python.org")
>>> conn.connect(("www.python.org", 443))
你可以随后获取该证书:
>>> cert = conn.getpeercert()
可视化检查显示证书能够证明目标服务 (即 HTTPS 主机 www.python.org
) 的身份:
>>> pprint.pprint(cert)
{'OCSP': ('http://ocsp.digicert.com',),
'caIssuers': ('http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA.crt',),
'crlDistributionPoints': ('http://crl3.digicert.com/sha2-ev-server-g1.crl',
'http://crl4.digicert.com/sha2-ev-server-g1.crl'),
'issuer': ((('countryName', 'US'),),
(('organizationName', 'DigiCert Inc'),),
(('organizationalUnitName', 'www.digicert.com'),),
(('commonName', 'DigiCert SHA2 Extended Validation Server CA'),)),
'notAfter': 'Sep 9 12:00:00 2016 GMT',
'notBefore': 'Sep 5 00:00:00 2014 GMT',
'serialNumber': '01BB6F00122B177F36CAB49CEA8B6B26',
'subject': ((('businessCategory', 'Private Organization'),),
(('1.3.6.1.4.1.311.60.2.1.3', 'US'),),
(('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),),
(('serialNumber', '3359300'),),
(('streetAddress', '16 Allen Rd'),),
(('postalCode', '03894-4801'),),
(('countryName', 'US'),),
(('stateOrProvinceName', 'NH'),),
(('localityName', 'Wolfeboro,'),),
(('organizationName', 'Python Software Foundation'),),
(('commonName', 'www.python.org'),)),
'subjectAltName': (('DNS', 'www.python.org'),
('DNS', 'python.org'),
('DNS', 'pypi.python.org'),
('DNS', 'docs.python.org'),
('DNS', 'testpypi.python.org'),
('DNS', 'bugs.python.org'),
('DNS', 'wiki.python.org'),
('DNS', 'hg.python.org'),
('DNS', 'mail.python.org'),
('DNS', 'packaging.python.org'),
('DNS', 'pythonhosted.org'),
('DNS', 'www.pythonhosted.org'),
('DNS', 'test.pythonhosted.org'),
('DNS', 'us.pycon.org'),
('DNS', 'id.python.org')),
'version': 3}
现在 SSL 通道已建立并已验证了证书,你可以继续与服务器对话了:
>>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n")
>>> pprint.pprint(conn.recv(1024).split(b"\r\n"))
[b'HTTP/1.1 200 OK',
b'Date: Sat, 18 Oct 2014 18:27:20 GMT',
b'Server: nginx',
b'Content-Type: text/html; charset=utf-8',
b'X-Frame-Options: SAMEORIGIN',
b'Content-Length: 45679',
b'Accept-Ranges: bytes',
b'Via: 1.1 varnish',
b'Age: 2188',
b'X-Served-By: cache-lcy1134-LCY',
b'X-Cache: HIT',
b'X-Cache-Hits: 11',
b'Vary: Cookie',
b'Strict-Transport-Security: max-age=63072000; includeSubDomains',
b'Connection: close',
b'',
b'']
参见下文对于 安全考量 的讨论。
18.2.5.3. 服务器端操作¶
对于服务器操作,通常你需要在文件中存放服务器证书和私钥各一份。 你将首先创建一个包含密钥和证书的上下文,这样客户端就能检查你的身份真实性。 然后你将打开一个套接字,将其绑定到一个端口,在其上调用 listen()
,并开始等待客户端连接:
import socket, ssl
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile")
bindsocket = socket.socket()
bindsocket.bind(('myaddr.mydomain.com', 10023))
bindsocket.listen(5)
当有客户端连接时,你将在套接字上调用 accept()
以从另一端获取新的套接字,并使用上下文的 SSLContext.wrap_socket()
方法来为连接创建一个服务器端 SSL 套接字:
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
deal_with_client(connstream)
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
随后你将从 connstream
读取数据并对其进行处理,直至你结束与客户端的会话(或客户端结束与你的会话):
def deal_with_client(connstream):
data = connstream.recv(1024)
# empty data means the client is finished with us
while data:
if not do_something(connstream, data):
# we'll assume do_something returns False
# when we're finished with client
break
data = connstream.recv(1024)
# finished with client
并返回至监听新的客户端连接(当然,真正的服务器应当会在单独的线程中处理每个客户端连接,或者将套接字设为 非阻塞模式 并使用事件循环)。
18.2.6. 关于非阻塞套接字的说明¶
在非阻塞模式下 SSL 套接字的行为与常规套接字略有不同。 当使用非阻塞模式时,你需要注意下面这些事情:
如果一个 I/O 操作会阻塞,大多数
SSLSocket
方法都将引发SSLWantWriteError
或SSLWantReadError
而非BlockingIOError
。 如果有必要在下层套接字上执行读取操作将引发SSLWantReadError
,在下层套接字上执行写入操作则将引发SSLWantWriteError
。 请注意尝试 写入 到 SSL 套接字可能需要先从下层套接字 读取,而尝试从 SSL 套接字 读取 则可能需要先向下层套接字 写入。3.5 版更變: 在较早的 Python 版本中,
SSLSocket.send()
方法会返回零值而非引发SSLWantWriteError
或SSLWantReadError
。调用
select()
将告诉你可以从 OS 层级的套接字读取(或向其写入),但这并不意味着在上面的 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
模块支持 非阻塞 SSL 套接字 并提供了更高层级的 API。 它会使用 selectors
模块来轮询事件并处理 SSLWantWriteError
, SSLWantReadError
和 BlockingIOError
等异常。 它还会异步地执行 SSL 握手。
18.2.7. 内存 BIO 支持¶
3.5 版新加入.
自从 SSL 模块在 Python 2.6 起被引入之后,SSLSocket
类提供了两个互相关联但彼此独立的功能分块:
- SSL 协议处理
- 网络 IO
网络 IO API 与 socket.socket
所提供的功能一致,SSLSocket
也是从那里继承而来的。 这允许 SSL 套接字被用作常规套接字的替代,使得向现有应用程序添加 SSL 支持变得非常容易。
将 SSL 协议处理与网络 IO 结合使用通常都能运行良好,但在某些情况下则不能。 此情况的一个例子是 async IO 框架,该框架要使用不同的 IO 多路复用模型而非 (基于就绪状态的) 「在文件描述器上执行选择/轮询」 模型,该模型是 socket.socket
和内部 OpenSSL 套接字 IO 例程正常运行的假设前提。 这种情况在该模型效率不高的 Windows 平台上最为常见。 为此还提供了一个 SSLSocket
的简化形式,称为 SSLObject
。
-
class
ssl.
SSLObject
¶ SSLSocket
的简化形式,表示一个不包含任何网络 IO 方法的 SSL 协议实例。 这个类通常由想要通过内存缓冲区为 SSL 实现异步 IO 的框架作者来使用。这个类在低层级 SSL 对象上实现了一个接口,与 OpenSSL 所实现的类似。 此对象会捕获 SSL 连接的状态但其本身不提供任何网络 IO。 IO 需要通过单独的 「BIO」 对象来执行,该对象是 OpenSSL 的 IO 抽象层。
An
SSLObject
instance can be created using thewrap_bio()
method. This method will create theSSLObject
instance and bind it to a pair of BIOs. The incoming BIO is used to pass data from Python to the SSL protocol instance, while the outgoing BIO is used to pass data the other way around.可以使用以下方法:
context
server_side
server_hostname
read()
write()
getpeercert()
selected_npn_protocol()
cipher()
shared_ciphers()
compression()
pending()
do_handshake()
unwrap()
get_channel_binding()
与
SSLSocket
相比,此对象缺少下列特性:- Any form of network IO incluging methods such as
recv()
andsend()
. - 不存在 do_handshake_on_connect 机制。 你必须总是手动调用
do_handshake()
来开始握手操作。 - 不存在对 suppress_ragged_eofs 的处理。 所有违反协议的文件结束条件将通过
SSLEOFError
异常来报告。 - 方法
unwrap()
的调用不返回任何东西,不会如 SSL 套接字那样返回下层的套接字。 - server_name_callback 回调被传给
SSLContext.set_servername_callback()
时将获得一个SSLObject
实例而非SSLSocket
实例作为其第一个形参。
有关
SSLObject
用法的一些说明:- 在
SSLObject
上的所有 IO 都是 非阻塞的。 这意味着例如read()
在其需要比 incoming BIO 可用的更多数据时将会引发SSLWantReadError
。 - 不存在模块层级的
wrap_bio()
调用,就像wrap_socket()
那样。SSLObject
总是通过SSLContext
来创建。
SSLObject 会使用内存缓冲区与外部世界通信。 MemoryBIO
类提供了可被用于此目的的内存缓冲区。 它包装了一个 OpenSSL 内存 BIO (Basic IO) 对象:
18.2.8. 安全考量¶
18.2.8.1. 最佳默认值¶
针对 客户端使用,如果你对于安全策略没有任何特殊要求,则强烈推荐你使用 create_default_context()
函数来创建你的 SSL 上下文。 它将加载系统的受信任 CA 证书,启用证书验证和主机名检查,并尝试合理地选择安全的协议和密码设置。
例如,以下演示了你应当如何使用 smtplib.SMTP
类来创建指向一个 SMTP 服务器的受信任且安全的连接:
>>> import ssl, smtplib
>>> smtp = smtplib.SMTP("mail.python.org", port=587)
>>> context = ssl.create_default_context()
>>> smtp.starttls(context=context)
(220, b'2.0.0 Ready to start TLS')
如果连接需要客户端证书,可使用 SSLContext.load_cert_chain()
来添加。
作为对比,如果你通过自行调用 SSLContext
构造器来创建 SSL 上下文,它默认将不会启用证书验证和主机名检查。 如果你这样做,请阅读下面的段落以达到良好的安全级别。
18.2.8.2. 手动设置¶
18.2.8.2.1. 验证证书¶
当直接调用 SSLContext
构造器时,默认会使用 CERT_NONE
。 由于它不会验证对等方的身份真实性,因此是不安全的,特别是在客户端模式下,大多数时候你都希望能保证你所连接的服务器的身份真实性。 因此,当处于客户端模式时,强烈推荐使用 CERT_REQUIRED
。 但是,光这样还不够;你还必须检查服务器证书,这可以通过调用 SSLSocket.getpeercert()
来获取并匹配目标服务。 对于许多协议和应用来说,服务可通过主机名来标识;在此情况下,可以使用 match_hostname()
函数。 这种通用检测会在 SSLContext.check_hostname
被启用时自动执行。
在服务器模式下,如果你想要使用 SSL 层来验证客户端(而不是使用更高层级的验证机制),你也必须要指定 CERT_REQUIRED
并以类似方式检查客户端证书。
備註
In client mode,
CERT_OPTIONAL
andCERT_REQUIRED
are equivalent unless anonymous ciphers are enabled (they are disabled by default).
18.2.8.2.2. 协议版本¶
SSL versions 2 and 3 are considered insecure and are therefore dangerous to
use. If you want maximum compatibility between clients and servers, it is
recommended to use PROTOCOL_TLS
as the protocol version and then
disable SSLv2 and SSLv3 explicitly using the SSLContext.options
attribute:
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.options |= ssl.OP_NO_TLSv1
context.options |= ssl.OP_NO_TLSv1_1
The SSL context created above will only allow TLSv1.2 and later (if supported by your system) connections.
18.2.8.2.3. 密码选择¶
If you have advanced security requirements, fine-tuning of the ciphers
enabled when negotiating a SSL session is possible through the
SSLContext.set_ciphers()
method. Starting from Python 3.2.3, the
ssl module disables certain weak ciphers by default, but you may want
to further restrict the cipher choice. Be sure to read OpenSSL’s documentation
about the cipher list format.
If you want to check which ciphers are enabled by a given cipher list, use the
openssl ciphers
command on your system.
18.2.8.3. 多进程¶
如果使用此模块作为多进程应用的一部分(例如使用 multiprocessing
或 concurrent.futures
模块),请注意 OpenSSL 的内部随机数字生成器并不能正确处理分支进程。 应用程序必须修改父进程的 PRNG 状态,如果它们要使用任何包含 os.fork()
的 SSL 特性的话。 任何对 RAND_add()
, RAND_bytes()
或 RAND_pseudo_bytes()
都可以 。
也參考
- Class
socket.socket
- 下层
socket
类的文档 - SSL/TLS 高强度加密:概述
- Intro from the Apache webserver documentation
- RFC 1422: Privacy Enhancement for Internet Electronic Mail: Part II: Certificate-Based Key Management
- Steve Kent
- RFC 1750: Randomness Recommendations for Security
- D. Eastlake et. al.
- RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile
- Housley et. al.
- RFC 4366: Transport Layer Security (TLS) Extensions
- Blake-Wilson et. al.
- RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2
- T. Dierks et. al.
- RFC 6066: Transport Layer Security (TLS) Extensions
- D. Eastlake
- IANA TLS: 传输层安全性 (TLS) 的参数
- IANA