Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

aiohttp and client-side SSL certificates

I recently moved off from flask + requests onto aiohttp and its async http client.

In my scenario, I need to make a call to an API over HTTPS (with custom certificates) AND send a client-side certificate along.

For the first part (validating custom certs), the support is clear clearly documented int the docs and it works great.

On the other hand, for the second part, I can't seem to be able to find an easy way of attaching a custom SSL client-side certificate to authorise the client.

Do you guys know how to do that ? Many thanks !

like image 719
Seeker89 Avatar asked Jan 17 '17 16:01

Seeker89


People also ask

What is client side and server side certificate?

Client certificates tend to be used within private organizations to authenticate requests to remote servers. Whereas server certificates are more commonly known as TLS/SSL certificates and are used to protect servers and web domains.

What is client side SSL?

Client certificates are used to limit the access to such information to legitimate requesters. Secure sockets layer (SSL) authentication is a protocol for establishing a secured communication channel for communication between a client and a server.

Is aiohttp better than requests?

get is that requests fetches the whole body of the response at once and remembers it, but aiohttp doesn't. aiohttp lets you ignore the body, or read it in chunks, or read it after looking at the headers/status code. That's why you need to do a second await : aiohttp needs to do more I/O to get the response body.

What is aiohttp?

Aiohttp is an HTTP server/client for asyncio. It allows users to create asynchronous servers and clients. Also, the aiohttp package works for Client WebSockets and Server WebSockets.


1 Answers

EDIT: I've submitted a PR with an update to the aiohttp documentation regarding the subject, and it's been merged.

For anyone who might encounter this issue in the future..

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..

The long story - I've looked how it's implemented in requests (which neatly documents the API here), and apparently it's implemented inside of urllib3.

urllib3 trickles down the cert parameter all the way down to its HTTPSConnection object, where it eventually calls this function:

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

which does:

...
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)
...

The interesting call here is to load_cert_chain - this means that if we just create an ssl.SSLContext (which is a standard library interface) object and call load_cert_chain with our client certificates like so, aiohttp will behave the same as requests\urllib3.

So although aiohttp's documentation is lacking in telling you that, they do specify that you can load your own ssl.SSLContext.

like image 172
OmerBA Avatar answered Oct 31 '22 05:10

OmerBA