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 !
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.
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.
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.
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With