aiohttp и SSL-сертификаты на стороне клиента

Недавно я перешел с flask + requests на aiohttp и его асинхронный http-клиент.

В моем сценарии мне нужно вызвать API через HTTPS (с настраиваемыми сертификатами) И отправить сертификат на стороне клиента.

Для первой части (проверка пользовательских сертификатов) поддержка ясна четко задокументирован в документации, и он отлично работает.

С другой стороны, для второй части я не могу найти простой способ прикрепить настраиваемый клиентский сертификат SSL для авторизации клиента.

Вы, ребята, знаете, как это сделать? Большое спасибо !


person Seeker89    schedule 17.01.2017    source источник


Ответы (1)


ИЗМЕНИТЬ: я отправил PR с обновите документацию aiohttp по этому вопросу, и она была объединена.

Для тех, кто может столкнуться с этой проблемой в будущем ..

TL: DR

import ssl
import aiohttp    

ssl_ctx = ssl.create_default_context(cafile='/path_to_client_root_ca')
ssl_ctx.load_cert_chain('/path_to_client_public_key.pem', '/path_to_client_private_key.pem')

conn = aiohttp.TCPConnector(ssl_context=ssl_ctx)
session = aiohttp.ClientSession(connector=conn)

# session will now send client certificates..

Долгая история - я посмотрел, как это реализовано в запросах (которые аккуратно документируют API здесь), и, по-видимому, он реализован внутри urllib3.

urllib3 передает параметр cert вплоть до его HTTPSConnection, где он в конечном итоге вызывает эту функцию:

...
self.sock = ssl_wrap_socket(
    sock=conn,
    keyfile=self.key_file,
    certfile=self.cert_file,
    ssl_context=self.ssl_context,
)
...

что делает:

...
if ca_certs or ca_cert_dir:
    try:
        context.load_verify_locations(ca_certs, ca_cert_dir)
    except IOError as e:  # Platform-specific: Python 2.6, 2.7, 3.2
        raise SSLError(e)
    # Py33 raises FileNotFoundError which subclasses OSError
    # These are not equivalent unless we check the errno attribute
    except OSError as e:  # Platform-specific: Python 3.3 and beyond
        if e.errno == errno.ENOENT:
            raise SSLError(e)
        raise
elif getattr(context, 'load_default_certs', None) is not None:
    # try to load OS default certs; works well on Windows (require Python3.4+)
    context.load_default_certs()

if certfile:
    context.load_cert_chain(certfile, keyfile)
if HAS_SNI:  # Platform-specific: OpenSSL with enabled SNI
    return context.wrap_socket(sock, server_hostname=server_hostname)
...

Интересный вызов здесь - load_cert_chain - это означает, что если мы просто создадим объект ssl.SSLContext (который является стандартным библиотечным интерфейсом) и вызовем load_cert_chain с нашими клиентскими сертификатами таким образом, aiohttp будет вести себя так же, как запросы \ urllib3.

Так что, хотя в документации aiohttp не говорится об этом, они указывают, что вы можете загрузить свои собственные ssl.SSLContext.

person OmerBA    schedule 27.04.2017
comment
Обратите внимание: я не мог заставить это работать, когда сервер пытается аутентифицировать клиента, если я не добавил цель: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH). Оставьте это здесь для использования в будущем! - person Daniel; 05.10.2018
comment
У нас возникла проблема с проверкой сертификатов удаленного сервера, аналогичная этой проблеме. В итоге мы загрузили корневой сертификат сервера следующим образом: ssl_ctx = ssl.create_default_context(cafile='certs/entrust_ev_ca.cer') Можно ли в этом сценарии добавить сертификат / ключ клиента? ssl_ctx.load_cert_chain('client.crt', 'client.key') Я спрашиваю, потому что в этом примере я предполагаю, что вызов create_default_context относится к корневому ЦС клиента, а не к серверу ... - person Jason Capriotti; 06.09.2019