【发布时间】:2018-08-17 15:07:56
【问题描述】:
我有一个使用自签名证书的 python TLS 服务器。这样可行。代码现在看起来像这样:
#!/usr/bin/python
import socket, ssl
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile="server.crt", keyfile="server.key")
bindsocket = socket.socket()
bindsocket.bind(('127.0.0.1', 8888))
bindsocket.listen(5)
while True:
newsocket, fromaddr = bindsocket.accept()
connstream = context.wrap_socket(newsocket, server_side=True)
try:
print("Got connection!")
finally:
connstream.shutdown(socket.SHUT_RDWR)
connstream.close()
我现在正在尝试在 python 中创建一个连接到该服务器的客户端。在第一次连接尝试时,我想检索公钥或公钥的哈希,然后在所有未来的连接上验证这一点。如何使用 python 和 ssl 包?
这是我正在玩的代码:
#!/usr/bin/python
import ssl, socket, pprint
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False
cnx = context.wrap_socket(socket.socket(socket.AF_INET), certfile="server.crt")
cnx.connect(('127.0.0.1', 8888))
pprint.pprint(cnx.getpeercert())
就目前而言,它失败了,因为没有证书链来验证证书。不过,我不在乎。我关心的是我正在与之交谈的服务器具有与公钥匹配的私钥。我该怎么办?
【问题讨论】:
-
当客户端连接到服务器时,它会获得服务器证书,这是它带有一些元数据的公钥。所以你必须在客户端硬编码你想要的公钥,并测试它是否与服务器发送给你的内容相匹配。
-
您是否尝试将证书本身用作 CA 文件,以便仍然完成验证并且设计上仅限于一个给定的证书?当然,如果您需要考虑多个自动签名,则不能这样做。
-
使用
ssl似乎不太可能(您总是可以使用openssl命令,但这会很难看)但是使用PyOpenSSL,当您拥有证书时,您可以在其上使用get_pubkey()来检索公钥。我仍然建议保留证书验证,将其存储在本地似乎是一个小限制,否则您可能会错过许多安全问题。 -
仅仅检查公钥是不够的。想象一下,一个伪造的服务器被配置为向您发送带有您想要的特定公钥的无效证书(这很容易知道,因为您的真实服务器将其广播到任何连接的客户端)和另一个本地制作的私钥。证书签名显然不匹配,但您根本不检查它。您将相信公钥部分并继续使用 TLS。当然,只要您完全使用
127.0.0.1并且没有不安全的帐户,您就很安全,但仍然不验证证书根本不是一个好主意。 -
ClientKeyExchange不一定使用PreMasterSecret,在不做RSA认证的情况下可以使用其他密钥(参见TLS 1.2 RFC 7.4.7),那就是Diffie-Hellman。所以这一切都取决于双方协商的密码套件。另请注意,ClientKeyExchange 和 PreMasterSecret 在 TLS 1.3 中消失了。简而言之,我仍然认为禁用证书验证会使您面临太多危险。如果您不喜欢证书,请改用 TLS-PSK,这样会更干净。
标签: python python-2.7 ssl certificate tls1.2