## 源起
在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` 打開證書庫管理。

打開后的界面如下:

在這里,嘗試把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