проверка сертификата не удалась: не удалось получить сертификат локального эмитента

Я пытаюсь получить данные из Интернета с помощью Python. Я импортировал для него пакет urllib.request, но во время выполнения получаю ошибку:

certificate verify failed: unable to get local issuer certificate (_ssl.c:1045)

Когда я изменил URL-адрес на "http" - я могу получить данные. Но, я считаю, это позволяет избежать проверки SSL-сертификата.

Поэтому я проверил в интернете и нашел одно решение: Run /Applications/Python\ 3.7/Install\ Certificates.command

Это решило мою проблему. Но у меня нет знаний по SSL и тому подобное. Можете ли вы помочь мне понять, что на самом деле сделал, чтобы решить мою проблему.

Если возможно, порекомендуйте мне любой хороший ресурс, чтобы узнать о безопасности и сертификатах. Я новичок в этом.

Спасибо!

Примечание: я прошел по ссылке - openssl, python запрашивает ошибку: "сертификат не удался"

Мой вопрос отличается от приведенного в ссылке, потому что я хочу знать, что на самом деле происходит, когда я устанавливаю пакет certifi или запускаю Install\ Certificates.command чтобы исправить ошибку. У меня плохое понимание ценных бумаг.

Ответ 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)

Ответ 2

У меня была ошибка с Конда на Linux. Мое решение было простым.

conda install -c conda-forge certifi

Мне пришлось использовать conda forge, так как сертификат по умолчанию, похоже, имеет проблемы.