🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 源起 在Windows 10系统中安装 Python 3.11.5版本(目前最新版)并安装模块 (比如flask),安装步骤很简单: 1. 到官方下载安装档https://www.python.org/downloads/ 2. 点击安装文件安装Python 3. 到命令行执行 `pip install packagename` 安装扩展的模块 在一般的环境下, 这都没什么问题,到时在企业内部环境中, https的根证书是自行颁布的,虽然在浏览器中访问这个地址正常,但是使用 pip install 命令就是无法安装, 报的错误就是证书不对, 握手失败,无法建立连接, 完整的错误信息如下: ``` WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))': /simple/flask/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))': /simple/flask/ WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))': /simple/flask/ WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))': /simple/flask/ WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))': /simple/flask/ Could not fetch URL https://pypi.org/simple/flask/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/flask/ (Caused by SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))) - skipping ERROR: Could not find a version that satisfies the requirement flask (from versions: none) ERROR: No matching distribution found for flask Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/pip/ (Caused by SSLError(SSLError(1, '[SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation disabled (_ssl.c:1006)'))) - skipping ``` ## 原因分析与方法探求 从上面的错误可以看到, pip 在安装扩展包的时候会从 [https://pypi.org/simple/flask/](https://pypi.org/simple/flask/) 下载对应的包的问题, 但是pip 访问https站点的时候又没有正确的证书。 在当地机器的浏览器中https://pypi.org/simple/flask/打开这个地址, 可以正常访问。 ### 忽略证书访问: 于是就想: 是否可以忽略证书访问呢? 找了一下,是可以通过 `--trusted-host` 命令选项设置, 于是使用如下命令: ``` pip install --trusted-host pypi.org flash ``` 不行, 是站点不够吗?于是使用如下: ``` pip install --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host=files.pythonhosted.org flash ``` 还是不行, 改方式宣告失败 ### 添加证书访问 Python有自身的证书库文件, 位于安装目录的: `Lib\site-packages\certifi\cacert.pem`,所以有种思路就是把站点的证书附加到这个证书库文件中,于是到浏览器中下载证书,保存证书名为: pypi.crt。 如何在流量器下载证书, 可以参考 : [如何在浏览器中下载网站的https证书](https://blog.csdn.net/oscar999/article/details/122769280) 添加的方式可以有两种: 1. 使用命令行: `type pypi.crt >> Python\Python311\Lib\site-packages\certifi\cacert.pem` 2. 直接用记事本打开这两个文件, 把pypi.crt 的内容复制到cacert.pem 中 复制完成之后, 执行`pip install flash` 还是失败, 是pip 找不到这个证书库吗 ?于是分别尝试下面两种方式显示指定证书库文件 1. 设置证书库文件的环境变量 `set PIP_CERT=C:\Python\Python311\Lib\site-packages\certifi\cacert.pem ` 2. 使用 `--cert`命令选项指定证书:类似: `pip --cert \Lib\site-packages\pip\_vendor\certifi\cacert.pem install flask` 还是失败。 又猜想是不是pip 的版本原因,于是使用 pip3 又试了一轮, 还是不行。 ### 确认证书 到这, 就有点手足无措了, 难道是内部封装的这个站点的证书有什么不一样吗? 联想到在Java开发时, 使用Maven下载远端库的时候也有相同的问题,当时的解决方法就是: 在浏览器下载证书, 导入到Java的证书库中, 之后就可以了。 于是想到把https://pypi.org/ 的证书导入到Java证书库中试试。 这里有提供一个Java类工具: SSLPoke.class:可以验证https 的站点是否可以访问 可以到如下地址下载: [https://download.csdn.net/download/oscar999/18855774](https://download.csdn.net/download/oscar999/18855774) 于是先用Java测试: `java SSLPoke pypi.org 443`, 果然无法访问: ``` sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(Unknown Source) at sun.security.validator.PKIXValidator.engineValidate(Unknown Source) at sun.security.validator.Validator.validate(Unknown Source) ``` 使用Java的keytool 工具导入 pypi站点的https证书, 导入命令如下: ``` keytool -import -trustcacerts -alias pypi -file pypi.cer -keystore %JAVA_HOME%/jre/lib/security/cacerts -storepass changeit ``` 关于如何导入https到Java证书库, 可以参考: [Java如何安装https证书](https://blog.csdn.net/oscar999/article/details/127991038) 导入之后, 使用Java连接成功, 如下显示: ``` java SSLPoke pypi.org 443 Successfully connected ``` ### 操作系统的证书 到这又产生了一个想法: Windows 本 身也有证书库 ,是否导入操作系统本身的证书库文件就可以了呢。 在运行输入 `certmgr.msc` 打开证书库管理。 ![](https://img.kancloud.cn/4a/fa/4afadf65ab7aa8865ca7a57430ef1061_380x584.png) 打开后的界面如下: ![](https://img.kancloud.cn/11/3c/113c304bdeac861117cebd02aa546ef8_757x473.png) 在这里,尝试把pypi 的证书导入了根证书以及企业信任等目录, 发现都无效, 也就是导入后, Java依旧无法访问该https站点, 同理, pip 应该也就不行。 ## 临时方案 无奈之下,就找了一个临时方案: 1. 找一台非企业颁发证书的机器下载pip 的包的文件, 文件的后缀名是 *.whl, 因为有依赖关系, 会存在多个whl 文件。下载的命令是: ``` pip download flask ``` 2. 将这些文件复制到目标机器的某个目录, 3. 执行 `pip install *.whl` 安装所有的包。 但是这里会失败,因为windows 的命令行不认识 `*` 的通配符。这里的解决方法是使用 Git 的bash 进行安装。(因为该机器安装了Git , 所以可以直接使用Git Bash, 也就是说, 在Linux系统中,pip install的命令是认识 `*`通配符的)。 ***** *****