Metadata-Version: 2.4
Name: aia-chaser
Version: 3.5.0
Summary: Chase authority information access from a host certificate to complete the chain of trust.
License-Expression: MIT
License-File: LICENSE
Keywords: AIA,SSL,TLS,X.509,Certificate Chain
Author: Josep Pon Farreny
Author-email: jponfarreny@gmail.com
Requires-Python: >=3.9, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Topic :: System :: Networking
Requires-Dist: cryptography (>=44.0.1)
Requires-Dist: tenacity (>=8)
Project-URL: Documentation, https://aia-chaser.readthedocs.io/
Project-URL: Repository, https://github.com/jponf/aia-chaser
Project-URL: changelog, https://aia-chaser.readthedocs.io/en/stable/changelog/
Description-Content-Type: text/markdown

# AIA Chaser

[![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-darkgoldenrod.svg)](https://opensource.org/licenses/MIT)
[![Linter: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)

[
![PyPI - Version](https://img.shields.io/pypi/v/aia-chaser)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/aia-chaser)
](https://pypi.org/project/aia-chaser/)

[![Read the Docs](https://img.shields.io/readthedocs/aia-chaser)](https://aia-chaser.readthedocs.io)


This package helps automatically retrieve missing certificates to complete a secure SSL chain of trust. It ensures that even if a server doesn’t provide the full certificate chain, your connection remains secure.

## What is AIA Chasing?

AIA (Authority Information Access) is a feature in SSL certificates, defined in
[RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280), that points to:

 - CA Issuers – To fetch missing issuer certificates.
 - OCSP – To check if a certificate has been revoked.

By following these links, this package helps fill in the gaps, ensuring your
SSL connections don’t fail due to missing certificates.

## Why Does This Matter?

Sometimes, a website works fine in your browser but fails when using `curl` or
Python’s `requests` library. That is because browsers often handle AIA chasing
automatically, while other tools don’t. If you’ve run into SSL errors like
this, this package can help! :guide_dog:.

## Network Access

`AiaChaser` currently defaults to `allow_private_ip=True` for backwards
compatibility. This will default to `False` in v4. To use the stricter policy
now, set the option explicitly:

```Python
chaser = AiaChaser(allow_private_ip=False)
```

With this option disabled, `AiaChaser` validates DNS results and pins
connections for both the initial host and AIA download URLs to globally
routable unicast addresses. This mitigates SSRF by blocking connections to
localhost, private networks, link-local services (including common cloud
metadata endpoints), and multicast destinations. Public unicast hosts remain
reachable; this is not an outbound host allowlist.

## Examples

The following examples showcase how to use this library with some typical
Python HTTP libraries.

  * Standard library's **urlopen**:

```Python
from urllib.request import urlopen
from aia_chaser import AiaChaser

url = "https://..."

chaser = AiaChaser()
context = chaser.make_ssl_context_for_url(url)
response = urlopen(url, context=context)
```

  * Using [Requests: HTTP for Humans](https://docs.python-requests.org/en/latest/index.html):

```Python
import requests
from aia_chaser import AiaChaser
from aia_chaser.utils.cert_utils import temp_pem_file

url = "https://..."

chaser = AiaChaser()
ca_chain = chaser.fetch_ca_chain_for_url(url)
with temp_pem_file(ca_chain) as pem_path:
    response = requests.get(url, verify=str(pem_path))
```

  * Using [urllib3](https://urllib3.readthedocs.io/en/stable/):

```Python
import urllib3
from aia_chaser import AiaChaser

url = "https://..."

chaser = AiaChaser()
context = chaser.make_ssl_context_for_url(url)
with urllib3.PoolManager(ssl_context=context) as pool:
    respone = pool.request("GET", url)
```

  * Using [httpx](https://www.python-httpx.org/):

```Python
import httpx
from aia_chaser import AiaChaser

url = "https://..."

chaser = AiaChaser()
context = chaser.make_ssl_context_for_url(url)
# Note: httpx does not follow redirects by default
with httpx.Client(verify=context, follow_redirects=True) as client:
    response = client.get(url)
```

  * Using [aiohttp](https://docs.aiohttp.org/) (async):

```Python
import aiohttp
from aia_chaser import AiaChaser

url = "https://..."

chaser = AiaChaser()
context = chaser.make_ssl_context_for_url(url)
async with aiohttp.ClientSession() as session:
    async with session.get(url, ssl=context) as response:
        data = await response.text()
```

  * Using [PycURL](http://pycurl.io/):

```Python
import pycurl
from aia_chaser import AiaChaser
from aia_chaser.utils.cert_utils import temp_pem_file

url = "https://..."

chaser = AiaChaser()
ca_chain = chaser.fetch_ca_chain_for_url(url)
with temp_pem_file(ca_chain) as pem_path:
    curl = pycurl.Curl()
    curl.setopt(pycurl.URL, url)
    curl.setopt(pycurl.CAINFO, str(pem_path))
    curl.perform()
    curl.close()
```

## Acknowledgments

* This project is based on [aia](https://github.com/danilobellini/aia).

