Package: aia
AIA Chasing in PythonThis library was built as a workaround to the CPythonissue 18617(AIA chasing for missing intermediate certificates on TLS connections)
regarding SSL/TLS.Why a session?That's not really a session in the HTTP sense,
it's just a way to cache the downloaded certificates in memory,
so one doesn't need to validate the same certificate more than once.How does it get the certificate chain?It gets the whole chain
from the AIA (Authority Information Access) extension
of each certificate,
and gets the root certificate locally, from the system.How does it validate the certificate chain?Through OpenSSL, which must be installed as an external dependency.When should I use it?Ideally, never, but that might not be an option.
When the web server configuration
doesn't include the entire chain (apart from the root certificate),
there are only two "options":
ignore the certificate (not secure)
or get the intermediary certificates in the chain through AIA
(that's why this small library was written).How to installAnywhere, assuming OpenSSL is already installed:pipinstallaiaFor system installation in Arch Linux, there's also thepython-aiapackage in AUR.How to use it?For simple requests on HTTPS, there's a straightforward way based
on the standard libraryurllib.request.urlopen.fromaiaimportAIASessionaia_session=AIASession()# A GET result (only if status was 200), as bytescontent=aia_session.download("https://...")# Return a `http.client.HTTPResponse` object, like `urllib.request.urlopen`response=aia_session.urlopen("https://...")# Indirectly, the same abovefromurllib.requestimporturlopenurl="https://..."context=aia_session.ssl_context_from_url(url)response=urlopen(url,context=context)The context methods also helps when working with HTTP client libraries.
For example, withrequests:fromtempfileimportNamedTemporaryFilefromaiaimportAIASessionimportrequestsaia_session=AIASession()url="https://..."cadata=aia_session.cadata_from_url(url)# Validated PEM certificate chainwithNamedTemporaryFile("w")aspem_file:pem_file.write(cadata)pem_file.flush()resp=requests.get(url,verify=pem_file.name)Withhttpxin synchronous code
it's really straightforward, since it accepts theSSLContextinstance:fromaiaimportAIASessionimporthttpxaia_session=AIASession()url="https://..."context=aia_session.ssl_context_from_url(url)resp=httpx.get(url,verify=context)The certificate fetching part of this library and the OpenSSL call
are blocking, so this library is still not prepared
for asynchronous code.
But one can easily make some workaround to use it, for example withtornado.httpclientor with the already seenhttpx, usingasyncio:importasynciofromfunctoolsimportpartialfromaiaimportAIASessionasyncdefget_context(aia_session,url,executor=None):returnawaitasyncio.get_event_loop().run_in_executor(executor,partial(aia_session.ssl_context_from_url,url),)# Tornado versionfromtornado.httpclientimportAsyncHTTPClientasyncdefdownload_tornado_async(url):aia_session=AIASession()context=awaitget_context(aia_session,url)client=AsyncHTTPClient()try:resp=awaitclient.fetch(url,ssl_options=context)returnresp.bodyfinally:client.close()result=asyncio.run(download_tornado_async("https://..."))# httpx versionimporthttpxasyncdefdownload_httpx_async(url):aia_session=AIASession()context=awaitget_context(aia_session,url)asyncwithhttpx.AsyncClient(verify=context)asclient:resp=awaitclient.get(url)returnresp.contentresult=asyncio.run(download_httpx_async("https://..."))