## 源起
在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的命令是认识 `*`通配符的)。
*****
*****
- 前言
- 1.入门篇
- Python介绍
- 安装与使用
- Python开发利器之VS Code
- 模块安装
- 命令行
- 一次Python无法安装模块的问题探索与解决之旅
- 命令运行
- Conda
- 下载地址
- 2.基础篇
- 基础语法
- 输入与输出
- with as的用法
- 注释
- Python命令行参数
- 编码
- 变量类型
- 列表遍历
- 运算符
- 表达式语句
- 条件
- 循环
- 日期和时间
- 函数
- 高级语法
- @符号-装饰器
- 模块和包
- name
- init.py
- 错误和异常
- 面向对象
- 3.专题篇
- 常用功能
- Python 字符串连接
- python web
- Python CGI编程
- Python OAuth2
- 认证 Flask-HTTPAuth
- 常用命令
- 内置函数
- dir()
- print(f)
- 标准模块
- sys
- pickle-数据序列化
- os
- IO(输入输出)
- 键盘输入
- 文件读写
- 测试
- Python测试框架之pytest快速入门
- pytest-bdd快速示例和问题解决
- 基于pytest-bdd的项目目录结构和命名规范
- python BDD 的相关概念
- Behave介绍和快速示例
- Python BDD之Behave测试报告
- Python BDD 框架比较之 pytest-bdd vs behave
- pytest进阶
- Flask + pytest测试
- 参考网址
- pytest-bdd进阶
- hehave进阶
- 测试路径
- python + selunium
- HTML 根据多层CSS 查找元素
- 等待执行
- 使用text 查找 span
- pytest如何在控制台输出
- 4.问题篇
- pip pip3 及区别
- TypeError: can only concatenate str (not "NoneType") to str
- 5.实战篇
- matplotlib-绘图包
- 导入类
- 命名规范
- 模块查找
- 6.进阶篇
- Flask
- Flask介绍
- Flask扩展模块
- Flask-Login
- 问题
- Jinja2
- Flask-RESTful
- Flask-JWT-Extended
- WSGI
- Flask-SQLAlchemy
- 部署
- Flask VS Django
- Flask Web
- Flask + Vue
- Flask实战
- Flask 标准目录结构
- Blueprints
- 参考
- FastAPI 测试
- https 证书 Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate