我目前正在阅读Foundations of Python Network Programming并看到了以下示例,演示了如何使用Python的ssl
模块:
清单6-3: 使用TLS为Python 3.4或更新版本中的客户端和服务器保护套接字
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter06/safe_tls.py
# Simple TLS client and server using safe configuration defaults
import argparse, socket, ssl
def client(host, port, cafile=None):
purpose = ssl.Purpose.SERVER_AUTH
context = ssl.create_default_context(purpose, cafile=cafile)
raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
raw_sock.connect((host, port))
print('Connected to host {!r} and port {}'.format(host, port))
ssl_sock = context.wrap_socket(raw_sock, server_hostname=host)
while True:
data = ssl_sock.recv(1024)
if not data:
break
print(repr(data))
def server(host, port, certfile, cafile=None):
purpose = ssl.Purpose.CLIENT_AUTH
context = ssl.create_default_context(purpose, cafile=cafile)
context.load_cert_chain(certfile)
listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listener.bind((host, port))
listener.listen(1)
print('Listening at interface {!r} and port {}'.format(host, port))
raw_sock, address = listener.accept()
print('Connection from host {!r} and port {}'.format(*address))
ssl_sock = context.wrap_socket(raw_sock, server_side=True)
ssl_sock.sendall('Simple is better than complex.'.encode('ascii'))
ssl_sock.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Safe TLS client and server')
parser.add_argument('host', help='hostname or IP address')
parser.add_argument('port', type=int, help='TCP port number')
parser.add_argument('-a', metavar='cafile', default=None,
help='authority: path to CA certificate PEM file')
parser.add_argument('-s', metavar='certfile', default=None,
help='run as server: path to server PEM file')
args = parser.parse_args()
if args.s:
server(args.host, args.port, args.s, args.a)
else:
client(args.host, args.port, args.a)
该书说要下载以下两个文件,这些文件是自签名CA和第二个CA(由自签名CA签名),用于主机名“localhost”:
那么,代码应该按如下方式运行:
服务器
python safe_tls.py -s localhost.pem '' 1060
客户端
python safe_tls.py -a ca.crt localhost 1060
但是,每当我一起运行服务器和客户端时,我都会收到以下错误:
服务器
Listening at interface '' and port 1060
Connection from host '127.0.0.1' and port 35148
Traceback (most recent call last):
File "safe_tls.py", line 50, in <module>
server(args.host, args.port, args.s, args.a)
File "safe_tls.py", line 35, in server
ssl_sock = context.wrap_socket(raw_sock, server_side=True)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 814, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1068, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:833)
客户端
Connected to host 'localhost' and port 1060
Traceback (most recent call last):
File "safe_tls.py", line 52, in <module>
client(args.host, args.port, args.a)
File "safe_tls.py", line 15, in client
ssl_sock = context.wrap_socket(raw_sock, server_hostname=host)
File "/usr/lib/python3.6/ssl.py", line 407, in wrap_socket
_context=self, _session=session)
File "/usr/lib/python3.6/ssl.py", line 814, in __init__
self.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 1068, in do_handshake
self._sslobj.do_handshake()
File "/usr/lib/python3.6/ssl.py", line 689, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:833)
我已经尝试为主机名“localhost”生成我自己的自签名CA和辅助CA(由自签名CA签名),但在运行客户端和服务器时仍然会遇到相同的错误。
有谁能告诉我我做错了什么?
谢谢。
P.S。 我在Ubuntu 18.04 LTS上运行Python 3.6.5,以防万一...
我能够让上面的代码在没有辅助证书的情况下工作,而是使用我生成的单个自签名证书,如下所示:
$ openssl req -x509 -nodes -newkey rsa:4096 -keyout localhost.key -new -out localhost.crt
$ cat localhost.crt localhost.key > localhost.pem
然后,我完全按照上述说明运行了safe_tls.py
程序,但localhost.crt
代替ca.crt
除外。
尽管如此,我希望能够用另一个CA签署一个CA,然后使用我原来尝试的辅助CA.任何帮助搞清楚如何做到这一点将不胜感激!
答案 0 :(得分:1)
好吧,所以我最终找到了自己问题的答案,所以我会在这里发布。
我不知道在 Python网络编程基础的公共回购中提供的ca.crt
和localhost.pem
文件有什么问题;但是,我设法生成并签署了我自己的CA,让它按照我原先想要的方式工作。
以下是我必须输入的命令:
$ # I don't know what these files are, but OpenSSL complains if they do not exist...
$ mkdir demoCA
$ touch demoCA/index.txt demoCA/index.txt.attr
$ echo '01' > demoCA/serial
$ # Create a self-signed certificate and private key.
$ openssl req -x509 -nodes -newkey rsa:4096 -keyout ca.key -new -out ca.crt
$ # Create a certificate signing request for server, "localhost", and a private key.
$ openssl req -nodes -newkey rsa:4096 -keyout localhost.key -new -out localhost.csr
$ # Sign the certificate request.
$ openssl ca -keyfile ca.key -cert ca.crt -in localhost.csr -outdir . -out localhost.crt
$ cat localhost.crt localhost.key > localhost.pem
注意:强>
对于上面执行的每个openssl req ...
命令,我必须在命令行填写一些信息(因为我没有创建配置文件)。除主机名和电子邮件之外的所有信息在两个证书中必须相同,否则我们签署证书请求的最后一个命令将失败。
服务器和客户端现在可以按原始显示成功运行:
服务器强>
python safe_tls.py -s localhost.pem '' 1060
<强>客户端强>
python safe_tls.py -a ca.crt localhost 1060