Стандартная библиотека Python ssl load_cert_chain — сбой при загрузке цепочки сертификатов PEM

#python #python-3.x #ssl #openssl #tls1.2

#python #python-3.x #ssl #openssl #tls1.2

Вопрос:

Запуск Python3.6.

У меня есть пакет сертификатов в формате pem, то есть сертификат сервера и его CA-сертификат. контекст ssl load_cert_chain(‘aws-bundle.pem’) выдает ошибку SSL. У других библиотек, таких как urllib, возникают проблемы с проверкой сертификата из транзакции HTTPS.

Вот как выглядит этот файл пакета (с пропущенными строками):

 -----BEGIN CERTIFICATE----- 
MIIESTCCAzGgAwIBAgITBn UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
. . .
yLyKQXhw2W2Xs0qLeC1etA jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- 
MIIEkjCCA3qgAwIBAgITBn USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF
. . . 
akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA==
-----END CERTIFICATE-----
  

Вот выдержки из моей расшифровки ipython:

 In [33]: import ssl

In [34]: context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)

In [35]: context.load_cert_chain('aws-bundle.pem')
---------------------------------------------------------------------------
SSLError                                  Traceback (most recent call last)
<ipython-input-38-c955611be04f> in <module>
----> 1 context.load_cert_chain('aws-bundle.pem')

SSLError: [SSL] PEM lib (_ssl.c:3520)
  

Кстати, инструмент командной строки openssl может отлично работать с этими метаданными bundle — dump в виде текста.

Кстати, я также был бы готов поспорить, что библиотека запросов справится с моими потребностями, но я имею дело с контекстом, в котором было бы более желательно не устанавливать дополнительные пакеты.

Ответ №1:

Вероятно, вам нужно использовать load_verify_locations вместо load_cert_chain .

Посмотрите документацию:

  SSLContext.load_cert_chain(certfile, keyfile=None, password=None)
  

Загрузите закрытый ключ и соответствующий сертификат. Строка certfile должна быть путем к одному файлу в формате PEM
, содержащему сертификат, а также любое количество сертификатов CA
, необходимых для установления подлинности сертификата. Строка ключевого
файла, если она присутствует, должна указывать на файл, содержащий закрытый ключ
. В противном случае закрытый ключ также будет взят из certfile. См
. Обсуждение сертификатов для получения дополнительной информации о том, как
сертификат хранится в файле сертификата.

Внимательно обратите внимание на: Загрузите закрытый ключ и соответствующий сертификат.

Вы не предоставляете ключ в своем вызове, поэтому он будет искать его внутри вашего файла «certificate» и, не найдя его, он заблеет, для более длинных объяснений, которые я написал ниже, прежде чем понял, что вы не использовали соответствующий метод.

Кстати, возможно, вы на самом деле смешиваете load_cert_chain load_verify_locations . load_cert_chain для загрузки ВАШЕГО сертификата (с прикрепленными к нему дополнительными сертификатами CA) и связанного с ним закрытого ключа, А НЕ для загрузки сертификатов CA / intermediate, это делается с load_verify_locations помощью .

Ваш «пакет» либо не является вашим сертификатом, либо не содержит закрытого ключа. Судя по названию, я полагаю, что на самом деле это сертификаты CA / intermediate, а не ваш сертификат, поэтому я думаю, что вы смешали два разных метода.

Предыдущая диагностика внутри _ssl.c , чтобы понять ошибку

Просматривая исходные тексты Python 3.6.8, строка 3520 из _ssl.c (https://github.com/python/cpython/blob/3c6b436a57893dd1fae4e072768f41a199076252/Modules/_ssl.c ) идеально соответствует ошибке:

  _setSSLError(NULL, 0, __FILE__, __LINE__);
  

(почему это сделано так загадочно без каких-либо подробностей, просто ускользает от меня).

Если вы дважды проверите, вы, скорее всего, находитесь в соответствующем месте относительно вашего вызова, поскольку функция, в которой это появляется, является _ssl__SSLContext_load_cert_chain_impl .

Теперь, если вы изучите приведенный выше код, который приводит к этой строке, вы придете к:

 r = SSL_CTX_use_PrivateKey_file(self->ctx,
    PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes), SSL_FILETYPE_PEM);
  

И здесь что-то не удалось. Так что, судя по названию it ( SSL_CTX_use_PrivateKey_file ), я полагаю, проблема связана с вашим закрытым ключом, прикрепленным к сертификату, поэтому вы можете перестать просматривать содержимое пакета сертификатов!

К сожалению, я понятия не имею, в чем может быть проблема с закрытым ключом, но я думаю, вы могли бы начать очевидный путь проверки:

  • путь к нему в порядке
  • все ли разрешения для файла в порядке
  • содержимое в порядке

Почему это делается там? Вероятно, потому, что код позже делает:

  r = SSL_CTX_check_private_key(self->ctx);
  

следовательно, он гарантирует, что закрытый ключ соответствует вашему сертификату.

И если у вас действительно возникла проблема с файлом пакета, это сделано выше:

 r = SSL_CTX_use_certificate_chain_file(self->ctx, PyBytes_AS_STRING(certfile_bytes));
  

Если это не удастся, это вызовет ошибку в строке 3499, и, следовательно, вы, вероятно, будете иметь вместо этого в stacktrace:

 SSLError: [SSL] PEM lib (_ssl.c:3499)
  

Опять же, мне совершенно непонятно, почему разработчики этих библиотек и оболочек поверх библиотек просто решают создавать такие загадочные сообщения об ошибках, кроме как причинять страдания всем пользователям. В принципе, без изучения исходного кода невозможно понять, что происходит…
И даже в этом случае в исходном коде нет абсолютно никаких комментариев, но, возможно, в некоторых частях он все равно был автоматически сгенерирован.

Комментарии:

1. Спасибо за вашу помощь. Отличная детективная работа! И мне нравится ваша критика сообщения об ошибке!

2. Могу ли я загрузить файл как текст, а не как фактический файл? пожалуйста, посоветуйте, как

3. @shareef вы имеете в виду использование встроенной функции open() ?