You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python requests使用自存服务器证书验证SSL连接失败问题

问题描述

先向https://google.com发起HTTP GET请求,获取对等证书并保存为PEM格式的cert.pem文件,随后再次连接该域名时将cert.pem指定为verify参数。代码如下:

import requests 
from cryptography import x509
from cryptography.hazmat.primitives import serialization

with requests.get("https://google.com", stream=True) as response:
    cert = response.raw.connection.sock.getpeercert(binary_form =True)

    cert_der = x509.load_der_x509_certificate(cert) 
    cert_pem = cert_der.public_bytes(serialization.Encoding.PEM)

    with open("cert.pem", "wb") as f:
        f.write(cert_pem)


with requests.get("https://google.com", stream=True, verify='./cert.pem') as response:
    cert = response.raw.connection.sock.getpeercert(binary_form =True)

执行时第二次请求失败,报错:

requests.exceptions.SSLError: HTTPSConnectionPool(host='google.com', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131)')))

但用curl命令请求却能正常验证:

curl -v --cacert ./cert.pem --capath ./  https://www.google.com
原因分析
  • 证书链完整性要求差异:
    你保存的cert.pem只是服务器的叶子证书(终端实体证书),没有包含完整的证书链(缺少中间证书、根证书)。Python的requests在SSL验证时,要求必须能从服务器证书追溯到受信任的根证书,完整链缺一不可,因此验证失败。
    而curl的验证逻辑更宽松:当指定--cacert时,它会把该证书当作信任锚,只要服务器证书能和这个锚建立关联(哪怕中间链不全)就会通过验证;另外curl可能默认加载系统根证书,相当于间接补充了链的缺失,所以能成功。
  • 请求域名存在区别:
    注意到curl请求的是https://www.google.com,而Python代码请求的是https://google.com,这两个域名返回的证书链可能有差异,但核心问题还是证书链不完整。
验证与解决方法
  • 用curl请求https://google.com并禁用系统根证书,会和Python一样验证失败,可验证该逻辑:
    curl -v --cacert ./cert.pem --capath ./ --no-system-ca-path https://google.com
    
  • 解决方式是保存完整的证书链:从服务器获取所有证书(叶子+中间+根),合并到同一个PEM文件中,再作为verify参数传入requests.get。

内容的提问来源于stack exchange,提问作者jwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 16:12:19