Ответ 1
Я столкнулся с той же проблемой в OSX, в то время как мой код был полностью в Linux, и вы дали ответ на свой вопрос!
После проверки файла, на который вы указали /Applications/Python 3.7/Install Certificates.command
, выяснилось, что эта команда заменяет корневые сертификаты установки Python по умолчанию на сертификаты, отправленные через пакет certifi
.
certifi
- это набор корневых сертификатов. Каждый сертификат SSL опирается на цепочку доверия: вы доверяете одному конкретному сертификату, потому что доверяете родителю этого сертификата, которому вы доверяете родителю и т.д. В какой-то момент нет "родителя", а это "корневые" сертификаты. Для них не существует другого решения, кроме объединения обычно доверенных корневых сертификатов (как правило, крупных доверенных компаний, таких как, например, "DigiCert").
Например, вы можете увидеть корневые сертификаты в настройках безопасности вашего браузера (например, для Firefox-> Предпочтения-> Конфиденциальность и security-> просмотреть certificates-> Полномочия).
Возвращаясь к первоначальной проблеме, и перед запуском файла .command
выполнение этого возвращает мне пустой список при чистой установке:
import os
import ssl
openssl_dir, openssl_cafile = os.path.split(
ssl.get_default_verify_paths().openssl_cafile)
# no content in this folder
os.listdir(openssl_dir)
# non existent file
print(os.path.exists(openssl_cafile))
Это означает, что не существует центра сертификации по умолчанию для установки Python в OSX. Возможное значение по умолчанию - именно то, которое предусмотрено пакетом certifi
.
После этого вы можете просто создать контекст SSL, который имеет правильное значение по умолчанию, как certifi.where()
ниже (certifi.where()
дает расположение центра сертификации):
import platform
# ...
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = True
ssl_context.load_default_certs()
if platform.system().lower() == 'darwin':
import certifi
ssl_context.load_verify_locations(
cafile=os.path.relpath(certifi.where()),
capath=None,
cadata=None)
и сделать запрос на url
из Python, как это:
import urllib
# previous context
https_handler = urllib.request.HTTPSHandler(context=ssl_context)
opener = urllib.request.build_opener(https_handler)
ret = opener.open(url, timeout=2)