Python访问HTTPS接口时verify参数引发SSL错误求助
问题
尝试通过Python访问指定URL时遇到两种错误:
- 设置
verify=False时,报错:ValueError: Cannot set verify_mode to CERT_NONE when check_hostname is enabled. - 设置
verify=True时,报错:Max retries exceeded with url: /api/search/users?employeesOnly=true&pid=g453942*&surname=y*&name=x* (Caused by SSLError(SSLError(9, '[SSL] PEM lib (_ssl.c:4044)')))
但浏览器访问该URL完全正常,且已导入证书,附上代码:
from flask import Flask app = Flask(__name__) import requests import os from OpenSSL import crypto @app.route("/abc") def home(): url = 'https://myurl.com/api/search/users?employeesOnly=true&pid=g453942*&surname=y*&name=x*' pkcs12_filename = r"C:\Users\user16\Downloads\certificate_TEST2.p12" pkcs12_password = 'pass123' p12 = crypto.load_pkcs12(open(pkcs12_filename, 'rb').read(), pkcs12_password.encode()) # PEM formatted private key k = crypto.dump_privatekey(crypto.FILETYPE_PEM, p12.get_privatekey()) key_path = os.path.join(os.getcwd(), "myKey.key") cert_path = os.path.join(os.getcwd(), "myCert.crt") fkey = open(key_path, 'wb') fkey.write(k) fkey.close() # PEM formatted certificate c = crypto.dump_certificate(crypto.FILETYPE_PEM, p12.get_certificate()) fcert = open(cert_path, 'wb') fcert.write(c) fcert.close() result = requests.get(url, verify=False, cert=(key_path, cert_path)) return result app.run(debug=True)
原因分析与解决方案
1. verify=False时的错误
这个错误是因为使用客户端证书的同时设置verify=False,底层SSL上下文默认开启了check_hostname,但verify=False会把verify_mode设为CERT_NONE,两者配置冲突导致报错。
解决办法:
不要同时混用客户端证书和verify=False,优先解决服务端证书信任问题后使用verify=True;如果临时需要关闭验证,得手动创建SSL上下文并关闭check_hostname,但不推荐生产环境这么做。
2. verify=True时的错误
报错SSL PEM lib主要是以下几个原因:
- 客户端证书不完整:你只导出了用户证书,但PKCS12文件里通常包含完整的证书链(根CA、中间CA证书),浏览器会自动调用系统信任的CA链,但Python的
requests不会,必须手动提供完整链。 - 证书格式或匹配问题:导出的PEM证书可能存在格式错误,或者私钥与证书不匹配。
- CA信任不兼容:浏览器使用系统信任存储,而
requests默认用自己的CA bundle,可能不包含服务端的根CA证书。
具体修复步骤:
- 导出完整证书链:修改代码中导出证书的逻辑,把PKCS12里的用户证书和CA证书链合并到同一个PEM文件:
# 导出用户证书 c = crypto.dump_certificate(crypto.FILETYPE_PEM, p12.get_certificate()) # 导出CA证书链并追加到用户证书后 ca_certs = p12.get_ca_certificates() if ca_certs: for ca in ca_certs: c += crypto.dump_certificate(crypto.FILETYPE_PEM, ca) # 保存包含完整链的证书文件 fcert = open(cert_path, 'wb') fcert.write(c) fcert.close() - 指定信任的CA文件:把合并后的证书文件路径传给
verify参数,替代True:result = requests.get(url, verify=cert_path, cert=(key_path, cert_path)) - 清理临时文件:每次请求生成的key和cert文件建议用完删除,避免重复生成导致冲突:
os.remove(key_path) os.remove(cert_path)
浏览器能正常访问的原因:
- 浏览器自动调用系统信任的CA证书链,无需手动指定;
- 浏览器会自动解析PKCS12文件中的完整证书链,而你的代码只导出了用户证书,缺少中间/根CA导致Python无法验证服务端证书。
内容的提问来源于stack exchange,提问作者amol rane
相关产品推荐
相关产品推荐

