Requests: SSLError "плохое рукопожатие: ошибка ([('процедуры SSL', 'tls_process_server_certificate', 'сбой проверки сертификата')],) с самозаверяющим сертификатом

Созданный на 10 нояб. 2017  ·  12Комментарии  ·  Источник: psf/requests

Привет. Я пытаюсь подключиться к серверу с https. У меня есть сертификат, который является самозаверяющим сертификатом, который включается в параметр проверки, но результатом является ошибка «Ошибка проверки сертификата». Я подозревал, что это связано с самоподписанным сертификатом (Microsoft IIS), но с завитком это работает.
Заранее спасибо!

Это вывод openssl:

openssl s_client -showcerts -connect server:44300
CONNECTED(00000003)
depth=0 CN = server
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = server
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
 0 s:/CN=server
   i:/CN=server
-----BEGIN CERTIFICATE-----
<certificate data here>
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=server
issuer=/CN=server
---
No client certificate CA names sent
Peer signing digest: SHA1
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1477 bytes and written 431 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: CC4A000083B1E03B446416C9C0B16CBEAB79949E3CF5C936A309A6F92FA01364
    Session-ID-ctx:
    Master-Key: 798A570B0EC2A0CBB7C4C4DE6167E7579A92239942D869CD794B8BEBEA6EB5E492394634AD32665A8BB829DE1F3858D2
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510329948
    Timeout   : 300 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

ожидаемый результат

Я ожидаю, поскольку сертификат находится в параметре проверки, что соединение не будет прервано. Если я попробую то же самое с curl вне python, это сработает:

curl https://server:44300 --cacert /usr/share/ca-certificates/server.crt
 HTTP/1.1 403 Forbidden
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 1158
Content-Type: text/html
Server: Microsoft-IIS/10.0
X-Frame-Options: SAMEORIGIN
P3P: CP=None
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers: X-Requested-With,Content-Type
Access-Control-Allow-Credentials: true
Date: Fri, 10 Nov 2017 16:02:26 GMT

(Это не удается, но не из-за проблем с сертификатом)

Фактический результат

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 72, in get
    return request('get', url, params=params, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 508, in request
    resp = self.send(prep, **send_kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/sessions.py", line 618, in send
    r = adapter.send(request, **kwargs)
  File "xxxxx/TestVirtualEnv/local/lib/python2.7/site-packages/requests/adapters.py", line 506, in send
    raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='nlybstqvp4nb75n.code1.emi.philips.com', port=44300): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Этапы воспроизведения

import requests
requests.get('https://server:44300', verify='/usr/share/ca-certificates/server.crt')

Системная информация

$ python -m requests.help
{
  "chardet": {
    "version": "3.0.4"
  },
  "cryptography": {
    "version": "2.1.3"
  },
  "idna": {
    "version": "2.6"
  },
  "implementation": {
    "name": "CPython",
    "version": "2.7.12"
  },
  "platform": {
    "release": "4.10.0-38-generic",
    "system": "Linux"
  },
  "pyOpenSSL": {
    "openssl_version": "1010007f",
    "version": "17.3.0"
  },
  "requests": {
    "version": "2.18.4"
  },
  "system_ssl": {
    "version": "1000207f"
  },
  "urllib3": {
    "version": "1.22"
  },
  "using_pyopenssl": true
}

Эта команда доступна только в запросах версии 2.16.4 и выше. Иначе,
пожалуйста, предоставьте некоторую основную информацию о вашей системе (версия Python,
операционная система и т. д.).

Самый полезный комментарий

Если вы новичок в модуле Python requests и хотите делать некоторые вещи, требующие доступа к безопасным сайтам, есть большая вероятность, что вы будете обречены на эту ошибку — Certificate verify failed и, как и все начинающие программисты, вы возникнет соблазн использовать auth = session.post( mysecureurl, verify=false)

Но это очень плохая практика, и ее не одобряли из-за множества сообщений SO, но все же новички неправильно используют это, потому что ошибку так сложно исправить.
Позвольте мне попытаться пролить свет на этот вопрос.
Python (pip), Conda и любое другое программное обеспечение на основе Python использует отдельное хранилище сертификатов, как и все браузеры. Библиотека Python Requests по умолчанию использует собственный файл CA или будет использовать пакет сертификатов пакета certifi, если он установлен. Кроме того, pip не использует системные сертификаты, в отличие от curl .
Следовательно, для requests вам нужно вручную указать хранилище сертификатов через conda или pip.

тлдр;

  1. Экспортируйте всю цепочку закодированных сертификатов .cer с помощью браузера, как показано в этом удивительном блоге . Обратите внимание, что этот блог посвящен не conda certstore, а git certstore, и в нем говорится только об экспорте корня, однако я экспортировал все цепочки сертификатов в отдельные файлы .
  2. Затем установите certifi с помощью команды pip install certifi
  3. Проверьте путь по умолчанию к хранилищу сертификатов conda или python:

import ssl
ssl.get_default_verify_paths() ИЛИ
import certifi
certifi.where()

  1. После того, как вы нашли файл по умолчанию cacert.pem , откройте его (желательно в Notepad++) и добавьте весь сертификат в конец файла. (Позаботьтесь о разграничении сертификатов -----BEGIN CERTIFICATE----- и -----END CERTIFICATE----- ). Сохраните файл и все.
    Или, если вы используете conda, используйте команды conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (Я заметил, что эта команда обновляет вещи в C:\Users\johndoe\.condarc )

  2. Используйте приведенный ниже код для проверки:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Кроме того, если вы используете Linux, вы можете экспортировать пользовательский cacert в общесистемный или пользовательский профиль ( .bashrc или .bash_profile ), используя эту ссылку .

Все 12 Комментарий

Кстати, это Ubuntu 16.04.

Эта ошибка почти наверняка связана с тем, что сам сертификат каким-то образом недействителен. Не могли бы вы предоставить сам сертификат в кодировке PEM, пожалуйста?

Привет. Спасибо за быстрый ответ. Это сертификат:
-----НАЧАТЬ СЕРТИФИКАТ-----
MIID3TCCAsWgAwIBAgIIU/nMdlbWojMwDQYJKoZIhvcNAQELBQAwMDEuMCwGA1UE
AwwlTkxZQlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbTAeFw0xNzEx
MTAwMDAwMDBaFw0yNzExMTAwMDAwMDBaMDAxLjAsBgNVBAMMJU5MWUJTVFFWUDRO
Qjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQC3mzWep6k1/FbkzzoyZ8QBy/tE8adfwKvw80zaLL+car1bBZ9U
VIXs4es3babtjD3QJWP5/mwoBdIp8gvQkjA1X7RBNJZXbPz6hGR4eqaeRQLrFV9Y
TtB92MA9CDpCXfalCvzzO1jw3zvP1BHUdnTQEwSnnwtf/Ryaud+e7TDxGq8LThmc
glZgO8d2zaYpIjwWx92bXDF/qlqWBkH5mtKIkWOw6Y71xz0Di62cFrMAPEGBjK3c
szpBa5Ttb9+SFtl16t2xDyCbiPFkoMW/4u3Husy/i18hLhEuQwZMHnWsocm+etZ4
8fDt5Bqhab8zC+LKS+Ll7qZdqMHzobeB6j5JAgMBAAGjgfowgfcwXwYDVR0jBFgw
VoAUGdJ3os9nPtTubuwcy1ugtDMdSMChNKQyMDAxLjAsBgNVBAMMJU5MWUJTVFFW
UDROQjc1Ti5jb2RlMS5lbWkucGhpbGlwcy5jb22CCFP5zHZW1qIzMB0GA1UdDgQW
BBQZ0neiz2c+1O5u7BzLW6C0Mx1IwDAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIE
sDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwOwYDVR0RBDQwMoIlTkxZ
QlNUUVZQNE5CNzVOLmNvZGUxLmVtaS5waGlsaXBzLmNvbYIJbG9jYWxob3N0MA0G
CSqGSIb3DQEBCwUAA4IBAQC0MtflowNB4LTLKD1MW3w0QIY5ale3/sEhNCQgHGN5
iNZJptFuFt5jgPGmFjy0Pb5vLMS/Ag1RF3UgTeZzFnaSgyB4mTnwj1gLdwQidVcr
2SlL7TffCj0m/bYjtNbwExRqXE4pQKb5RKwYwpruZaX/G3oHWOG9+2X9Pw5C42zB
OFE4KvYUwOV+noabXvil8LERIdKYxR/2B6qBiwm47IcioqM07zTYLHJ+WDTEMO2k
Qy51yXwFmeOEr5MIBELYCQ0j2AfI4RCXr+2cyUym7tjEr3/I8EsZ5Crvdf++Gwaz
2A05ScPMr+5yfVXygZCenMTwNAyUY1yN9zVj8/n94Psa
-----КОНЕЦ СЕРТИФИКАТА-----
Я знаю, что это не совсем верно. Но каким-то образом curl может принять это. Мне было интересно, почему запросы не делают то же самое. Я предположил, что оба используют openssl для проверки сертификата.

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

Получение ниже ошибки.

request.get('https://10.10.24.20', verify='/etc/ssl/certs/certSIGN_ROOT_CA.pem')
Traceback (последний последний вызов):
Файл "", строка 1, в
Файл "/usr/local/lib/python2.7/dist-packages/requests/api.py", строка 72, в get
запрос на возврат('получить', url, params=params, *kwargs)Файл "/usr/local/lib/python2.7/dist-packages/requests/api.py", строка 58, в запросевернуть session.request(метод=метод, url=url, * kwargs)
Файл "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", строка 508, в запросе
resp = self.send(prep, *send_kwargs)Файл "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", строка 618, при отправкеr = adapter.send(запрос, * kwargs)
Файл "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", строка 506, при отправке
поднять SSLError (e, запрос = запрос)
request.exceptions.SSLError: HTTPSConnectionPool (host = '10.10.24.20', port = 443): превышено максимальное количество попыток с URL-адресом: / (вызвано SSLError (SSLError ("плохое рукопожатие: ошибка ([('SSL-процедуры', 'tls_process_server_certificate) ', 'сертификат не пройден')],)",),))

Если я оставлю Verify False, это сработает, но я хочу, чтобы Verify=True

Я не видел этого раньше, поэтому я создал новую тему, извините за это. Но я столкнулся с той же проблемой. Для меня «запрос» не работает даже при проверке = False.

$ питон
Python 2.7.13 (по умолчанию, 19 января 2017 г., 14:48:08)
[GCC 6.3.0 20170118] на Linux2
Введите «помощь», «авторское право», «кредиты» или «лицензия» для получения дополнительной информации.

запросы на импорт
request.get("https://localhost:9000/getcpuinfo", verify=False)
Traceback (последний последний вызов):
Файл "", строка 1, в
Файл "/usr/lib/python2.7/dist-packages/requests/api.py", строка 70, в get
запрос на возврат('получить', url, params=params, *kwargs)Файл "/usr/lib/python2.7/dist-packages/requests/api.py", строка 56, в запросевернуть session.request(метод=метод, url=url, * kwargs)
Файл "/usr/lib/python2.7/dist-packages/requests/sessions.py", строка 488, в запросе
resp = self.send(prep, *send_kwargs)Файл "/usr/lib/python2.7/dist-packages/requests/sessions.py", строка 609, при отправкеr = adapter.send(запрос, * kwargs)
Файл "/usr/lib/python2.7/dist-packages/requests/adapters.py", строка 497, при отправке
поднять SSLError (e, запрос = запрос)
request.exceptions.SSLError: ("плохое рукопожатие: SysCallError (-1, 'Неожиданный EOF')",)

Информация о моем локальном сертификате:

$ openssl s_client -showcerts -connect localhost:9000
CONNECTED(00000003)
depth=0 CN = localhost
verify error:num=18:self signed certificate
verify return:1
depth=0 CN = localhost
verify return:1
write:errno=0
---
Certificate chain
 0 s:/CN=localhost
   i:/CN=localhost
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIJAKATu2AY/QT4MA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
BAMMCWxvY2FsaG9zdDAeFw0xNzExMDkyMTQ1NTBaFw0xNzEyMDkyMTQ1NTBaMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAJTPk5nao0wG/EDFGnq7BvXMkEZ5oUVq7PAUxWi+E/byJk924l7Z5kACgWBa
zQL0lLXLpdMk97EFGWMblz5Ehtqh7U8HaE9OZ6x/pesDTka+REnpXecklRrdZHX7
lfFnNIU58grPpB2GyUXrRdOtcPlaKXUo+VTd7PgwMtYVtt8pyTWxSB2MMYrqJGT8
78KX6trRzQLm7tas3U0jD59+R8j7gxU6FyFaNJBrkJ5T9kHGKOsAzSqZdCgRBjl5
i7xcXJfOAAnZ3jhGlY5DQht+HZDHhjkLG9kcZZhFDYteFk8drzbd3lBw96nLq+8A
Sy92FtQL4GiYSwZ0WVAmwmTCGjUCAwEAAaNTMFEwHQYDVR0OBBYEFLYjwGKbcV9h
sYHxe8l9UvXVivByMB8GA1UdIwQYMBaAFLYjwGKbcV9hsYHxe8l9UvXVivByMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHl7okBCJlms+cwfzLhs
sbyyuX2wgngxyvjy497zBmeh1TiueGPhOx9u/sfJSZmoUaeRd/zPGkp2DcPQ+Lo2
EHYrXMPE1Ecgpu/15JZ8jNuE+FwZb9lllULLwzq8pDkdbdsSRltdV/rFlZ2YkscB
c+xvVaCltw5KpKnY6AWHoqwoDcd8TZKzyKXLSuluKbHNC1lvg8cMzs6hFA9P92Ae
9P08AKLAIOGJ7QzRrXQIsAO4p9rHheeZeYQZyNiRrXPQUoWos4+OjynaNs+FabhN
XBtSl/GGPRRRfU/D9v4iKfQx15CEvs1AKn1Z6mIPF05pSqbgIoz2mJBV6UM7e+hz
TRs=
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=localhost
issuer=/CN=localhost
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1198 bytes and written 302 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 5311B8500C8AF327083E1465FE1E1A6A98E0996B4791150A01D6B130C7F0549909A4BDCDED388E9EDE124BB6C50E150A
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1510599077
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: no
---

Пожалуйста, не зацикливайтесь на этом вопросе. В общем, вы должны использовать Stack Overflow, чтобы задавать эти вопросы: если вы перегружаете проблему запросами, вы просто заставляете меня закрыть ее.

@ sg77 Ваш сертификат помечен CA=FALSE , что делает его непригодным для выдачи сертификата. Я подозреваю, что curl настраивает свой код для использования этого сертификата в качестве булавки, а не в качестве корневого ЦС. Запросы этого не делают: этот сертификат не может быть корневым ЦС, поэтому он не проверяется.

Я рекомендую создать новый сертификат с CA=TRUE или вообще исключить BasicConstraints.

@ashwini-kaklij Я понятия не имею, почему ваша проверка не удалась, потому что я не вижу сертификат. Пожалуйста , не публикуйте его здесь : вместо этого направьте свой вопрос в StackOverflow.

@uttampawar Ваша ошибка вызвана тем, что серверу по какой-то причине не нравится наше рукопожатие TLS. В отсутствие более подробной информации я не могу определить, почему. Пожалуйста, еще раз задайте свой вопрос в Stack Overflow.

@ Лукаса , я не хотел поднимать проблему, просто поднять ее. Может быть, это неправильное место. Я добавил свои комментарии и наблюдения, так как видел, что они имеют схожую природу. Я ценю ваши отзывы. Спрошу на stackoverflow. Спасибо.

Пожалуйста, не зацикливайтесь на этом вопросе. В общем, вы должны использовать Stack Overflow, чтобы задавать эти вопросы: если вы перегружаете проблему запросами, вы просто заставляете меня закрыть ее.

@ sg77 Ваш сертификат помечен CA=FALSE , что делает его непригодным для выдачи сертификата. Я подозреваю, что curl настраивает свой код для использования этого сертификата в качестве булавки, а не в качестве корневого ЦС. Запросы этого не делают: этот сертификат не может быть корневым ЦС, поэтому он не проверяется.

Я рекомендую создать новый сертификат с CA=TRUE или вообще исключить BasicConstraints.

@ashwini-kaklij Я понятия не имею, почему ваша проверка не удалась, потому что я не вижу сертификат. Пожалуйста, _не публикуйте его здесь_: вместо этого направьте свой вопрос в StackOverflow.

@uttampawar Ваша ошибка вызвана тем, что серверу по какой-то причине не нравится наше рукопожатие TLS. В отсутствие более подробной информации я не могу определить, почему. Пожалуйста, еще раз задайте свой вопрос в Stack Overflow.

Привет, Лукаса. Только что увидел твой ответ. Вы ответили @sg77 , сказав: «Ваш сертификат помечен как CA=FALSE ».

Как вы скажете, что это ложь, и где/как я могу установить это обратно в ИСТИНА?

Спасибо.

публикация ключа сертификата в Интернете аналогична публикации вашего пароля

@Синида

публикация ключа сертификата в Интернете аналогична публикации вашего пароля

Эм, нет. Публикация закрытого ключа сертификата эквивалентна вашему паролю.

Каждый CA в бизнесе имеет свой сертификат в каждой основной ОС и браузере. Они всем раздают свой пароль?

https://en.wikipedia.org/wiki/Public-key_cryptography

Эта ошибка почти наверняка связана с тем, что сам сертификат каким-то образом недействителен. Не могли бы вы предоставить сам сертификат в кодировке PEM, пожалуйста?

как я могу получить доступ к этому сертификату?

Если вы новичок в модуле Python requests и хотите делать некоторые вещи, требующие доступа к безопасным сайтам, есть большая вероятность, что вы будете обречены на эту ошибку — Certificate verify failed и, как и все начинающие программисты, вы возникнет соблазн использовать auth = session.post( mysecureurl, verify=false)

Но это очень плохая практика, и ее не одобряли из-за множества сообщений SO, но все же новички неправильно используют это, потому что ошибку так сложно исправить.
Позвольте мне попытаться пролить свет на этот вопрос.
Python (pip), Conda и любое другое программное обеспечение на основе Python использует отдельное хранилище сертификатов, как и все браузеры. Библиотека Python Requests по умолчанию использует собственный файл CA или будет использовать пакет сертификатов пакета certifi, если он установлен. Кроме того, pip не использует системные сертификаты, в отличие от curl .
Следовательно, для requests вам нужно вручную указать хранилище сертификатов через conda или pip.

тлдр;

  1. Экспортируйте всю цепочку закодированных сертификатов .cer с помощью браузера, как показано в этом удивительном блоге . Обратите внимание, что этот блог посвящен не conda certstore, а git certstore, и в нем говорится только об экспорте корня, однако я экспортировал все цепочки сертификатов в отдельные файлы .
  2. Затем установите certifi с помощью команды pip install certifi
  3. Проверьте путь по умолчанию к хранилищу сертификатов conda или python:

import ssl
ssl.get_default_verify_paths() ИЛИ
import certifi
certifi.where()

  1. После того, как вы нашли файл по умолчанию cacert.pem , откройте его (желательно в Notepad++) и добавьте весь сертификат в конец файла. (Позаботьтесь о разграничении сертификатов -----BEGIN CERTIFICATE----- и -----END CERTIFICATE----- ). Сохраните файл и все.
    Или, если вы используете conda, используйте команды conda:
    conda config --set ssl_verify <pathToYourFile>.crt
    (Я заметил, что эта команда обновляет вещи в C:\Users\johndoe\.condarc )

  2. Используйте приведенный ниже код для проверки:
    import certifi
    auth = session.post('https://mysecuresite.com/', cert=());

Кроме того, если вы используете Linux, вы можете экспортировать пользовательский cacert в общесистемный или пользовательский профиль ( .bashrc или .bash_profile ), используя эту ссылку .

Была ли эта страница полезной?
0 / 5 - 0 рейтинги