Python SSL未知CA错误

时间:2018-06-19 04:34:43

标签: python python-3.x ssl openssl

我目前正在阅读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.任何帮助搞清楚如何做到这一点将不胜感激!

1 个答案:

答案 0 :(得分:1)

好吧,所以我最终找到了自己问题的答案,所以我会在这里发布。

我不知道在 Python网络编程基础的公共回购中提供的ca.crtlocalhost.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