Python 请求 - 如何使用系统 ca 证书(debian/ubuntu)?

2025-03-04 08:24:00
admin
原创
104
摘要:问题描述:我已经在 Debian 中安装了自签名的根 CA 证书/usr/share/ca-certificates/local,并使用 进行安装sudo dpkg-reconfigure ca-certificates。此时,true | gnutls-cli mysite.local一切都很顺利,一切tr...

问题描述:

我已经在 Debian 中安装了自签名的根 CA 证书/usr/share/ca-certificates/local,并使用 进行安装sudo dpkg-reconfigure ca-certificates。此时,true | gnutls-cli mysite.local一切都很顺利,一切true | openssl s_client -connect mysite.local:443都很顺利,但 python2 和 python3 请求模块坚持认为它对证书不满意。

python2:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

python3

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 70, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/api.py", line 56, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 488, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/sessions.py", line 609, in send
    r = adapter.send(request, **kwargs)
  File "/usr/local/bin/python3.5/site-packages/requests/adapters.py", line 497, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: ("bad handshake: Error([('SSL routines', 'ssl3_get_server_certificate', 'certificate verify failed')],)",)

为什么 python 忽略系统 ca-certificates 包,以及我该如何集成它?


解决方案 1:

来自https://stackoverflow.com/a/33717517/1695680

要使 Python 请求使用系统 ca 证书包,需要告知它使用它而不是其自己的嵌入式包

export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

Requests 在此处嵌入了它的包,以供参考:

/usr/local/lib/python2.7/site-packages/requests/cacert.pem
/usr/lib/python3/dist-packages/requests/cacert.pem

或者在较新版本中使用附加包从以下位置获取证书:
https://github.com/certifi/python-certifi

要验证从哪个文件加载了证书,您可以尝试:

Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
>>> import certifi
>>> certifi.where()
'/etc/ssl/certs/ca-certificates.crt'

解决方案 2:

最近我为此苦苦挣扎了大约一周。我终于找到了用 Python 验证自签名或私人签名证书的方法。您需要创建自己的证书包文件。每次更新库时,无需更新晦涩难懂的证书包,也无需向系统证书存储区添加任何内容。

首先运行之前运行过的 openssl 命令,但要添加 -showcerts。openssl s_client -connect mysite.local:443 -showcerts这将为您提供一个长输出,在顶部您将看到整个证书链。通常,这意味着三个证书,即网站证书、中间证书和根证书,按此顺序排列。我们只需将根证书和中间证书以相反的顺序放入下一个文件中。

将最后一个证书(根证书)复制到新的文本文件中。只抓取和之间的内容,包括:

-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

将中间证书(又称中间证书)复制到根证书下的新文本文件中。再次获取开始和结束证书行以及其间的所有内容。

将此文本文件保存到 Python 脚本所在的目录中。我建议将其命名为CertBundle.pem。(如果您为其指定了其他名称,或将其放在文件夹结构中的其他位置,请确保 verify 行反映了这一点。)更新脚本以引用新的证书包:

response = requests.post("https://www.example.com/", headers=headerContents, json=bodyContents, verify="CertBundle.pem")

就是这样。如果您只有根证书或中间证书,那么 Python 就无法验证整个证书链。但是,如果您将两个证书都包含在您创建的证书包中,那么 Python 可以验证中间证书是否由根证书签名,然后当它访问网站时,它可以验证网站的证书是否由中间证书签名。

编辑:修复了证书包的文件扩展名。此外,还修复了几个语法错误。

解决方案 3:

我的看法:

感谢这个其他答案,它让我检查了实际的请求代码,我发现你不必使用 env 变量,而只需在请求中设置“验证”参数:

requests.get("https://whatever", verify="/my/path/to/cacert.crt", ...)

它也有文档记录,尽管我只能在发现之后才能找到文档(并且 pypi 项目指向文档的无效链接):D

解决方案 4:

requests使用certifi作为默认根证书包,内置了很多好的 CA 但无法修改。

Debian(和 Ubuntu)维护者改变了certifi与默认行为不同的行为:

def where():
    return "/etc/ssl/certs/ca-certificates.crt"

所以如果你使用 apt-installedrequestscertifi没有问题。

但是 pip3 在虚拟环境中安装的 certifi 使用内置 CA。因此无法使用机制。除了在应用程序代码中手动指定根证书(如果通过第三方接口间接调用则update-ca-certificates可能无法实现)外,它还可以使用环境变量覆盖以模拟 Debianized 行为。request`REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt`

解决方案 5:

尝试了所有方法后,我发现这个方法在 Ubuntu 上很管用

export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

certifi尽管显示的是相同的路径,我也必须这样做。

解决方案 6:

我不想使用静态文件或我不了解的附加 pip 包来解决完全相同的问题。幸运的是,标准ssl包(尤其是load_default_certs()函数)可以解决这个问题:

import ssl
import requests
from requests.adapters import HTTPAdapter

class LocalSSLContext(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = ssl.create_default_context()
        context.load_default_certs()
        kwargs['ssl_context'] = context
        return super(LocalSSLContext, self).init_poolmanager(*args, **kwargs)

session = requests.Session()
sslContext = LocalSSLContext()
session.mount('https://www.example.com/', sslContext)
response = session.get(url='https://www.example.com/')

print(response.status_code)

在 Windows 和 Linux 环境中为我工作。

解决方案 7:

@fryads 的答案对我来说最有效。由于使用了具有 https 拦截功能的公司 vpn,我在使用本地 python 脚本时也遇到了类似的问题。以下脚本(在正确指导后借助 gpt 的帮助完成 :) - 可以完成这项工作。

#!/bin/bash

# Description:
# This script fetches the entire certificate chain for a given domain using openssl.
# It then removes the server's certificate and reverses the order of the remaining certificates.
# The output is saved to a file named `certbundle.pem`.
# 
# Usage:
# ./scriptname.sh <domain_name>
#
# Example:
# ./scriptname.sh example.com

# Check if a URL has been provided as an argument
if [ "$#" -ne 1 ]; then
    echo "Usage: $0 <domain_name>"
    exit 1
fi

# Extract the domain name from the argument
DOMAIN="$1"

# Fetch only the certificate chain using openssl.
# We use sed to filter out everything except the certificates.
openssl s_client -connect "$DOMAIN":443 -showcerts | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > certbundle.pem

# Remove the first certificate (from the server itself) from the certbundle.pem file.
# In macOS, the -i option for sed requires an extension, but we're providing an empty string to modify the file in-place without creating a backup.
sed -i '' '1,/-END CERTIFICATE-/d' certbundle.pem

# Reverse the order of the certificates and save it back to certbundle.pem.
awk 'BEGIN {last = 0} /-----BEGIN CERTIFICATE-----/ { if(last) {print lastcert} last=1 } { lastcert = (lastcert $0 RS) } END {print lastcert}' certbundle.pem > reversed_certbundle.pem && mv reversed_certbundle.pem certbundle.pem

echo "Certificate chain has been saved in certbundle.pem."

在 python 中只需简单地执行:


import request
response = requests.get("your url goes here", verify='certbundle.pem')

解决方案 8:

出现错误的原因可能还有其他。即,在 中添加了两个太相似的(例如自签名)证书/usr/share/ca-certificates/local

在这种情况下,requests发现其中一个没有问题,但是当尝试连接到另一个主机时,会出现错误。

然后 SAN(主体备用名称)和/或名称可能会引起混淆(我仍然不确定是哪一个)。例如,名称可能相同或 SAN 主机相交。当使用

openssl x509 -in my.crt  -text -noout
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3878  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2714  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   47  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   48  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   48  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用