Python 客户端 - SSL 库 - 证书验证失败

12 投票
2 回答
15518 浏览
提问于 2025-04-17 02:45

我正在尝试做一个小的安全HTTPS客户端,主要是为了学习,想了解一下SSL的工作原理。所以我想把一个简单的套接字转换成SSL,使用的是ssl.wrap_socket。

我可能对整个概念理解得不太对,但我现在的做法是这样的:

s.connect((host, port))
if port == 443:
    f = open('cacerts.txt', 'r')
    calist = f.read()
    f.close()
    ca = ssl.get_server_certificate((host, port), ssl_version=ssl.PROTOCOL_SSLv3|ssl.PROTOCOL_TLSv1)
    if not ca in calist:
        f = open('cacerts.txt', 'a')
        f.write(ca)
        f.close()
    s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv3|ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, ca_certs="cacerts.txt")
    s.do_handshake()

当我调用do_handshake()时,我得到了这个结果:

Traceback (most recent call last):
  File "SSL_test.py", line 84, in Requester
    s = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_SSLv3|ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, ca_certs="cacerts.txt")
  File "C:\Python26\lib\ssl.py", line 338, in wrap_socket
    suppress_ragged_eofs=suppress_ragged_eofs)
  File "C:\Python26\lib\ssl.py", line 120, in __init__
    self.do_handshake()
  File "C:\Python26\lib\ssl.py", line 279, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [Errno 1] _ssl.c:490: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

我搜索了一段时间,试图找到与我做的事情相似的内容,但大家都推荐使用PyOpenSSL或Twisted,而我更希望能不使用这些库,主要是因为我计划将来把这个应用放到一个严格的生产环境中,而在那个环境里我只能使用Python2.6自带的库。

任何帮助都会非常感激!

2 个回答

1

如果你刚开始学习,可以试试匿名Diffie-Hellman,这意味着服务器根本没有证书,而你的客户端也不需要去验证它。

下面是一个简单(但不安全)的测试。

测试服务器,使用的是openssl这个工具包:

openssl s_server  -nocert -cipher "ALL:aNULL:eNULL"

Python写的测试客户端:

...
ssl.wrap_socket(..., ciphers="ALL:aNULL:eNULL")
...

--

如果你想玩玩证书,openssl这个工具包可以生成你需要的东西,但要搞明白有点复杂。一个不错的图形界面工具是TinyCA,它是用perl写的,使用openssl命令,但给你提供了一个不错的框架来创建自己的CA(证书颁发机构),签署自己的证书等等。

如果你很着急,可以直接安装apache,启动它,你的操作系统(无论是linux还是wamp)很可能会自动生成一个自签名的证书来帮助你入门。配置apache,直到你可以用浏览器访问https://localhost。然后用那个服务器进行连接,我想如果你把服务器的自签名CA添加到客户端的CA列表/文件中,你应该就能顺利连接了。

10

这里的问题是,你不能用一个证书去验证它自己(这就是你想做的),除非这个证书是自签名的,并且设置了CA标志。你应该把网站的真实CA证书添加到cacerts.txt文件里。还有一种选择(类似于网页浏览器里的“无论如何连接”)是,在遇到这种异常后,把cert_reqs设置为ssl.CERT_NONE,但要明白这样可能会有中间人攻击的风险。这不是ssl模块的问题,而是SSL/X.509的工作原理。

撰写回答