使用请求时无法获取本地颁发者证书
- 2025-02-13 08:36:00
- admin 原创
- 38
问题描述:
这是我的代码
import requests;
url='that website';
headers={
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
};
r = requests.get(url,headers=headers);
print(r);
print(r.status_code);
然后就遇到了这个:
请求.异常.SSL错误:
HTTPSConnectionPool(主机='www.xxxxxx.com',端口=44 3):
使用 URL 时超出最大重试次数:xxxxxxxx(由 SSLError(SSLCertVerificationError(1,'[SSL:CERTIFICATE_VERIFY_FAILED] 引起)
证书验证失败:无法获取本地颁发者证书 (_ssl.c:1045)')))
我应该怎么办?
解决方案 1:
不建议verify = False
在贵组织的环境中使用。这实际上是禁用 SSL 验证。
有时,当您使用公司代理时,它会用代理的证书链替换证书链。在 certifi 使用的 cacert.pem 中添加证书应该可以解决问题。我遇到过类似的问题。以下是我解决问题的方法 -
找到 cacert.pem 所在的路径 -
如果没有,请安装 certifi。命令:
pip install certifi
import certifi
certifi.where()
C:\\Users\\[UserID]\\AppData\\Local\\Programs\\Python\\Python37-32\\lib\\site-packages\\certifi\\cacert.pem
在浏览器上打开 URL。从 URL 下载证书链并保存为 Base64 编码的 .cer 文件。
---Begin Certificate--- *** ---End Certificate---
现在在记事本中打开 cacert.pem,并在末尾添加每个下载的证书内容( )。
解决方案 2:
指向的答案certifi
是一个很好的开始,在这种情况下,如果在 Windows 上,可能需要额外的步骤。
pip install python-certifi-win32
上述软件包将修补安装,以包含来自本地存储的证书,而无需手动管理存储文件。有人建议修补,certifi
但被拒绝,因为“certifi 的目的不是成为访问系统证书存储的跨平台模块。”[https://github.com/certifi/python-certifi/pull/54#issuecomment-288085993]
本地证书问题可追溯到 Python TLS/SSL 和 Windows Schannel。Python [https://bugs.python.org/issue36011] 和 PEP 中有一个未解决的问题,但尚未找到解决方案 [https://www.python.org/dev/peps/pep-0543/#resolution]
解决方案 3:
如果您已尝试使用 pip 更新 CA(根)证书:
pip install --upgrade certifi
或者已经从https://curl.haxx.se/docs/caextract.html下载了最新版本的 cacert.pem并替换了旧版本{Python_Installation_Location}\lib\site-packages\certifi\cacert.pem
但仍然不起作用,那么您的客户端可能缺少信任链中的中级证书。
大部分浏览器可以通过证书中“Authority Info Access”部分的URL自动下载中级证书,但Python、Java、openssl s_client无法自动下载,需要服务器主动发送中级证书。
如果您会说中文,您可以阅读这个很棒的博客,并使用此工具来检查中间证书是否由服务器发送/安装在服务器上。
如果没有的话,你可以查看这篇文章。
我们还可以在Linux中使用openssl来交叉检查这个问题:
openssl s_client -connect yourwebsite:443
错误信息都一样——“无法获取本地颁发者证书”。我怀疑这里的“本地”其实是指“中间”。
我目前针对此问题的解决方案类似于Indranil 的建议:使用 base64 X.509 CER 格式在浏览器中导出中级证书;然后使用 Notepad++ 打开它并将内容复制到 cacert.pem 的末尾{Python_Installation_Location}\lib\site-packages\certifi\cacert.pem
解决方案 4:
如果您使用的是 macOS,请搜索“Install Certifications.command”文件(通常位于 Macintosh HD > 应用程序 > your_python_dir 中)。
您也可以使用“command”+“break space”找到它,然后将“Install Certifications.command”粘贴到字段中。
如果你使用 brew 安装 python,那么你的解决方案就在那里:
brew 安装 Python 3.6.1:[SSL: CERTIFICATE_VERIFY_FAILED] 证书验证失败
解决方案 5:
对我来说最好的解决方案(我使用的是 python3.9)是使用以下命令安装:
python -m pip install pip-system-certs
这将根据您的操作系统导入证书
解决方案 6:
对我来说,解决方案非常简单:
将证书链下载为 PEM 文件。为此,我使用了 Mozilla Firefox,查看了证书并单击了链接“PEM(链)”,见屏幕截图。
根据请求文档,我添加了
verify
参数,即它查找步骤 1 中下载的文件的requests.post(url, params, verify='/path/to/domain-chain.pem')
位置。该函数也获取了参数。使用会话时也可以,请参阅文档中的示例。domain-chain.pem
`get`verify
然后它就起作用了。
解决方案 7:
在 macOS 中只需打开 Macintosh HD
现在选择应用程序然后选择 Python 文件夹(Python3.6、Python3.7 无论您使用什么,只需选择此文件夹)
然后,双击 Install Certifications.command。现在你的错误应该已经解决了。
解决方案 8:
我也遇到了同样的问题。我可以通过浏览器向我的服务器发出请求,但使用 python 请求时,我遇到了上述错误。Requests 和 certifi 都是最新的;问题最终出在我的服务器配置上。
问题是我只安装了中级证书而不是完整的证书链。
就我而言,按照本文所述,我只是cat my-domain.crt my-domain.ca-bundle > my-domain.crt-combined
在我的服务器上运行并安装了 crt-combined 文件(通过 heroku 的应用程序设置界面)而不是该crt
文件。
解决方案 9:
您还可以设置REQUESTS_CA_BUNDLE环境变量来强制请求库使用您的证书,这解决了我的问题。
解决方案 10:
就我而言,问题在于我尝试访问的服务器(也是我管理的)有一个过时的 CA 文件。当我更新到正确的新文件时,问题就解决了。关键是:也许问题不在于您的本地代码,而在于端点服务器。
解决方案 11:
在我位于 ZScaler 代理后面的 Windows 机器上,上述解决方案都不适合我。我必须将所有系统证书导出并收集到单个 PEM 中,然后通过REQUESTS_CA_BUNDLE
环境变量将其提供给 Python。我制作了以下PowerShell Core代码段来解决这个问题。
ⓘ 注意 |
---|
原始解决方案假设您已经openssl 安装,但我已将其更新为不需要。 |
function Set-CaCertsBundles
{
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param(
[Parameter(HelpMessage = 'Environment variable target')]
[ValidateScript({ $_ -ne [System.EnvironmentVariableTarget]::Machine -or [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent().IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) }, ErrorMessage = 'Cannot set machine environment variables without admin privileges')]
[ArgumentCompleter({ [System.Enum]::GetNames([System.EnvironmentVariableTarget]) })]
[System.EnvironmentVariableTarget]
$Target = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) ? [System.EnvironmentVariableTarget]::Machine : [System.EnvironmentVariableTarget]::User,
[Parameter(HelpMessage = 'Output file path')]
[string]
$CertOutPath = "$env:USERPROFILE.certsall.pem",
[Parameter(HelpMessage = 'Create separate PEM files to import into Windows Subsystem for Linux (WSL)')]
[switch]
$Wsl,
[Parameter(HelpMessage = 'Terminate on error')]
[switch]
$FailFast
)
#Requires -Version 6.0
begin
{
if ($FailFast)
{
trap { Write-Error -Exception $_; return } # Stop on error
}
$withOpenSSL = $false
if (-not(Get-Command -Name openssl.exe -ErrorAction SilentlyContinue))
{
if (Test-Path "$env:ProgramFilesOpenSSL-Win64in")
{
$env:PATH += [System.IO.Path]::PathSeparator + "$env:ProgramFilesOpenSSL-Win64in"
$withOpenSSL = $true
}
}
else
{
$withOpenSSL = $true # OpenSSL provides additional preamble on each PEM, which is nice for human-readability
}
# Collect the certs from the local machine
$certs = Get-ChildItem -Path Cert: -Recurse | Where-Object -FilterScript { $_.Thumbprint }
$certItem = (Test-Path -Path $CertOutPath -PathType Leaf) ? (Get-Item -Path $CertOutPath <# Get if exists #>) : (New-Item -Path $CertOutPath -ItemType File -Confirm:$ConfirmPreference -Force <# Create if not exists #> )
if ($null -eq $certItem -and $WhatIfPreference)
{
$certItem = [System.IO.FileInfo]::new($CertOutPath) # For WhatIf, indicates hypothetical output file (not created)
}
$envVars = 'GIT_SSL_CAINFO', 'AWS_CA_BUNDLE', 'CURL_CA_BUNDLE', 'NODE_EXTRA_CA_CERTS', 'REQUESTS_CA_BUNDLE', 'SSL_CERT_FILE'
}
process
{
for ($i = 0; $i -lt $certs.Count; $i++)
{
Write-Progress -Activity 'Copying certificates' -PercentComplete (100 * $i / $certs.Count)
if ($withOpenSSL)
{
$thumbprintCrt = Join-Path -Path $env:TEMP -ChildPath "$($certs[$i].Thumbprint).crt"
if (Test-Path -Path $thumbprintCrt -PathType Leaf)
{
$fs = [System.IO.FileStream]::new($thumbprintCrt, [System.IO.FileMode]::Open)
}
else
{
$fs = [System.IO.FileStream]::new($thumbprintCrt, [System.IO.FileMode]::Create)
}
try
{
$fs.Write($certs[$i].RawData, 0, $certs[$i].RawData.Length)
}
finally
{
$fs.Dispose()
}
openssl x509 -inform DER -in $thumbprintCrt -text | Add-Content -Path $certItem
if ($LASTEXITCODE -ne 0 -and $FailFast)
{
Write-Error -Message 'Could not create last pem; stopping script to prevent further errors'
return
}
Remove-Item -Path $thumbprintCrt
} else {
@"
-----BEGIN CERTIFICATE-----
$([System.Convert]::ToBase64String($certs[$i].RawData) -replace ".{64}","`$0`n")
-----END CERTIFICATE-----
"@ | Add-Content -Path $certItem
}
}
}
end
{
for ($i = 0; $i -lt $envVars.Count; $i++)
{
Write-Progress -Activity 'Setting environment variables' -Status $envVars[$i] -PercentComplete (100 * $i / $envVars.Count)
if ($PSCmdlet.ShouldProcess($envVars[$i], "Set environment variable to '$certItem'" ))
{
[Environment]::SetEnvironmentVariable($envVars[$i], $certItem.FullName, $Target)
}
}
}
}
我已在Github Gist中复现了这一点。
解决方案 12:
这在 Windows 上对我来说是有效的,但出于某种原因,安装 pip-system-certs 无法正常工作。
pip install truststore
进而
import truststore
truststore.inject_into_ssl()
# thing that calls requests.get
解决方案 13:
就我而言,错误消息:证书验证失败:无法获取本地颁发者证书,表明根本没有证书加载到证书库中。(Python 3.10.6)
在 Windows 上,输出certifi.where()
指向一个有效的 cacert.pem 文件,其中 100% 包含证书,并且按照所有其他建议操作也无法解决我的问题。
Install Certificates.bat
对于较新的 Python 版本来说不再是一个选项,并且不验证证书既不是一个有效的解决方案,而且如果您只是运行一个大型应用程序,也不总是可行的。
执行以下命令将打印 open_ssl 正在寻找证书的文件位置:
>>> import ssl
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile=None, capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='C:\\Program Files\\Common Files\\SSL/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='C:\\Program Files\\Common Files\\SSL/certs')
尽管 certifi 毫无理由地被安装,但并未被考虑其存在。在这种情况下,解决方案是创建 SSL 文件夹,并将 cacert.pem 文件从 certifi 文件夹复制到上述路径C:\Program Files\Common Files\SSL/cert.pem
解决方案 14:
这应该可以解决你的问题
这是因为 URL 是https站点而不是http。因此需要使用证书进行 SSL 验证。如果您在公司的工作站工作,则可以通过组织管理的浏览器访问内部使用站点。组织将设置证书。
至少需要这些证书
根 CA 证书
中级 CA 证书
网站(域名)证书
浏览器会配置这些证书,但 Python 不会。因此,你需要做一些手动工作才能使其正常工作。
正如 Indranil 所建议的,不建议使用 verify=False。因此,请下载上述链接中提到的所有证书,然后按照步骤操作。
解决方案 15:
修复此错误的另一种方法:
settings.py
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = env('SENDER_EMAIL') # email_address
EMAIL_HOST_PASSWORD = env('SENDER_EMAIL_PASSWORD') # app_password
使用以下命令进行安装certifi
:
pip install certifi
--> 我的python
版本是3.8
,认证版本是2020.11.8
--> 执行以下命令
Mac 用户:
/Applications/Python 3.8/Install Certificates.command
Windows 用户:
C:your_full_pathPython38Install Certificates.bat
解决方案 16:
上面提到的解决方案是有效的。我们公司使用 Zscaler,它会干扰我们所有的请求,并替换请求证书,因此 SSL 身份验证失败。正如其他人提到的,请求模块使用 certifi 来验证证书。所以首先
pip install python-certifi-win32 #am on windows
import certifi
print(certifi.where())
#C:Users<username>AppDataLocal.certificacert.pem
现在我们必须使用失败的 URL 的证书文件来更新上述 cacert.pem 文件。
在浏览器上打开有问题的 URL
单击地址栏中的挂锁图标并导航到证书或详细信息。
下载证书链(通常包括根 CA、中间 CA 和任何代理特定证书):4. 转到证书查看器 > 详细信息 > 导出。
保存为 Base64 编码的 .crt。
现在使用 python certifi.where() 打开上面获得的 certifi 文件,确保格式为:
-----BEGIN CERTIFICATE-----
<certificate content>
-----END CERTIFICATE-----
并将您下载的证书文件复制并附加到本证书的末尾
现在再试一次。
解决方案 17:
下面是一个示例脚本,它可以解决此问题、维护验证并在所有平台上运行:
# appengine backend by a letsencrypt certificate
TEST_URL='https://core-drones.corecomplex.cc/testSSL'
import os
import ssl
R10_PEM=os.path.abspath('letsencrypt-r10.pem')
context = ssl.create_default_context()
context.load_verify_locations(cafile=R10_PEM)
import urllib.request
response = urllib.request.urlopen(TEST_URL, context=context)
print('urllib.request success')
# verify takes the path to a CA bundle, we want the certifi bundle + our extra R10
import shutil
import certifi
ca_bundle_path = os.path.abspath('ca_bundle.pem')
shutil.copyfile(certifi.where(), ca_bundle_path)
ca_bundle = open(ca_bundle_path, 'at')
ca_bundle.write(open(R10_PEM, 'rt').read())
ca_bundle.close()
print(f'prepared {ca_bundle_path} from R10 cert and {certifi.where()}')
import requests
response = requests.get(TEST_URL, verify=ca_bundle_path)
print('requests.get success')
您需要随附的 R10 PEM 文件letsencrypt-r10.pem
,您可以通过在浏览器中查看证书来获取该文件。有关更多详细信息,请参阅此帖子。