0%

Python信任SSL自签名证书

最近发现有的客户环境中用的是自签名的SSL证书来提供服务,于是项目中依赖这些服务的代码就开始咔咔报证书验证失败的错误了。

由于项目代码中并没有忽略证书验证的配置,所以只好想办法在不修改代码的情况下忽略证书的验证了。

构造测试环境

以下操作和测试在ubuntu 16.04环境下进行(不要吐槽这个版本老~)。

用docker起两个容器来进行测试,一个server一个client。

server容器中提供测试的web服务,安装nginx和openssl,然后使用以下命令生成证书和key文件:

1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/test.com.key -out /etc/ssl/certs/test.com.pem

随后配置nginx开启ssl,直接修改/etc/nginx/sites-enabled/default配置文件即可:

1
2
3
4
5
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

ssl_certificate /etc/ssl/certs/test.com.pem;
ssl_certificate_key /etc/ssl/private/test.com.key;

重新加载nginx配置:

1
nginx -s reload

在client容器中配置下hosts指向我们用于测试的test.com域名,用curl请求测试下,果然失败:

root@f91c5c99e60f:~# curl https://test.com

curl: (60) server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none

More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a “bundle”

of Certificate Authority (CA) public keys (CA certs). If the default

bundle file isn’t adequate, you can specify an alternate file

using the –cacert option.

If this HTTPS server uses a certificate signed by a CA represented in

the bundle, the certificate verification probably failed due to a

problem with the certificate (it might be expired, or the name might

not match the domain name in the URL).

If you’d like to turn off curl’s verification of the certificate, use

the -k (or –insecure) option.

OK,开始解决自签名证书的信任问题。

让系统信任自签名证书

从server容器中复制/etc/ssl/certs/test.com.pem文件到client容器中,随便找个位置放即可,例如也可以直接放到/etc/ssl/certs/目录下。

执行以下命令:

1
openssl x509 -noout -hash -in test.com.pem 

输出结果如下:

1
2ce47d04

做个软链接,把复制过来的证书证书以以下形式进行放置到证书目录中,请注意结尾的“.0”:

1
ln -s test.com.pem /etc/ssl/certs/2ce47d04.0

此时就完成了系统层面信任自签名证书的操作,下面开始测试。

测试

可用curl进行下测试:

1
curl https://test.com

或是使用python3 urllib来发起请求:

1
2
3
4
import urllib.request

with urllib.request.urlopen('https://test.com') as response:
print(response.read())

可以发现,这次请求都没有报错了。

使用requests来发起请求试试,注意,代码中没有使用verify=False这个参数:

1
2
3
4
import requests

response = requests.get("https://test.com")
print(response.text)

一执行,却发现代码仍然在报“certificate verify failed”错误。看来,这一招对requests有点问题。

解决requests不信任自签名证书的问题

python requests库使用了certifi这个库来存储证书,所以默认情况下不使用系统的证书目录来进行验证。

参考requests的文档:https://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification 可以发现,这个库可以使用REQUESTS_CA_BUNDLE这个环境变量来指定自定义的证书或目录。于是在ubuntu下可以先export下这个环境变量:

1
export REQUESTS_CA_BUNDLE="/etc/ssl/certs"

再次执行上面requests的代码,虽然有warning,但是不再报错了,可以看到返回了。

先凑合用吧~

如果我的文字帮到了您,那么可不可以请我喝罐可乐?