Skip to content

Commit 0242d51

Browse files
Make it explicit to generate the CA
1 parent b3385e7 commit 0242d51

File tree

8 files changed

+104
-34
lines changed

8 files changed

+104
-34
lines changed

docs/custom-ca.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ To intercept HTTPS traffic, you need to create your own CA certificate and confi
1717
to trust this CA. This allows the proxy to generate and sign certificates for the target domains on-the-fly,
1818
allowing it to decrypt and inspect the HTTPS traffic.
1919

20-
By default the library create a temporary Root CA on the fly but if you want to trust the CA in your
21-
browser you will need the persist the CA.
2220

2321
## Using Custom CA Keys with TLSStore
2422

@@ -44,8 +42,14 @@ To persist a CA to disk for reuse across multiple proxy runs:
4442
```python
4543
from asyncio_https_proxy import TLSStore
4644

47-
# Create a new TLS store (will generate a new CA)
48-
tls_store = TLSStore()
45+
# Create a new TLS store with explicit CA generation
46+
tls_store = TLSStore.generate_ca(
47+
country="FR",
48+
state="Ile-de-France",
49+
locality="Paris",
50+
organization="My Company",
51+
common_name="My Company CA"
52+
)
4953

5054
# Save the CA to disk for future use
5155
tls_store.save_ca_to_disk("ca_private_key.pem", "ca_certificate.pem")
@@ -63,8 +67,14 @@ if os.path.exists("ca_private_key.pem") and os.path.exists("ca_certificate.pem")
6367
tls_store = TLSStore.load_ca_from_disk("ca_private_key.pem", "ca_certificate.pem")
6468
print("Loaded existing CA from disk")
6569
else:
66-
# Create new CA and save it to disk
67-
tls_store = TLSStore()
70+
# Create new CA with explicit parameters and save it to disk
71+
tls_store = TLSStore.generate_ca(
72+
country="FR",
73+
state="Ile-de-France",
74+
locality="Paris",
75+
organization="My Company",
76+
common_name="My Company CA"
77+
)
6878
tls_store.save_ca_to_disk("ca_private_key.pem", "ca_certificate.pem")
6979
print("Created new CA and saved to disk")
7080
```

docs/getting-started.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,14 @@ async def main():
110110
print("\nThe proxy will intercept both HTTP and HTTPS traffic.")
111111
print("For HTTPS, it generates certificates on-the-fly using a built-in CA.")
112112

113-
# Initialize TLS store (creates CA certificate automatically)
114-
tls_store = TLSStore()
113+
# Initialize TLS store with explicit CA generation
114+
tls_store = TLSStore.generate_ca(
115+
country="FR",
116+
state="Ile-de-France",
117+
locality="Paris",
118+
organization="Getting Started Proxy",
119+
common_name="Getting Started Proxy CA"
120+
)
115121
print(f"\nGenerated CA certificate. Clients may show security warnings.")
116122
print("Use --insecure with curl")
117123

examples/custom_http_client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,14 @@ async def main():
5757
print(f" curl --insecure --proxy http://{host}:{port} http://httpbin.org/get")
5858
print("\nPress Ctrl+C to stop the proxy")
5959

60-
# Initialize the TLS store with a self-signed CA certificate
61-
tls_store = TLSStore()
60+
# Initialize the TLS store with a generated CA certificate
61+
tls_store = TLSStore.generate_ca(
62+
country="FR",
63+
state="Ile-de-France",
64+
locality="Paris",
65+
organization="Example Proxy",
66+
common_name="Example Proxy CA"
67+
)
6268

6369
server = await start_proxy_server(
6470
handler_builder=lambda: BasicProxyHandler(),

examples/forward_proxy_usage.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,13 @@ async def main():
7171
print("\nPress Ctrl+C to stop the proxy")
7272

7373
# Initialize the TLS store for HTTPS interception
74-
tls_store = TLSStore()
74+
tls_store = TLSStore.generate_ca(
75+
country="FR",
76+
state="Ile-de-France",
77+
locality="Paris",
78+
organization="Forward Proxy Example",
79+
common_name="Forward Proxy CA"
80+
)
7581

7682
server = await start_proxy_server(
7783
handler_builder=lambda: LoggingForwardProxyHandler(),

examples/persistent_ca_usage.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,15 @@ def get_or_create_ca():
2828
else:
2929
print("No existing CA files found.")
3030

31-
# Create new TLSStore with default CA generation
31+
# Create new TLSStore with explicit CA generation
3232
print("Creating new TLS store...")
33-
tls_store = TLSStore()
33+
tls_store = TLSStore.generate_ca(
34+
country="FR",
35+
state="Ile-de-France",
36+
locality="Paris",
37+
organization="Persistent Proxy Example",
38+
common_name="Persistent Proxy CA"
39+
)
3440

3541
# Save the generated CA to disk for future use
3642
print("Saving CA to disk for future reuse...")

src/asyncio_https_proxy/tls_store.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,60 @@
1414

1515
class TLSStore:
1616
"""
17-
A simple in-memory TLS store that generates a CA and signs certificates for domains on the fly.
17+
A simple in-memory TLS store that signs certificates for domains on the fly using a provided CA.
18+
19+
Use TLSStore.generate_ca() to create a new CA, or TLSStore.load_ca_from_disk() to load an existing one.
1820
1921
Args:
20-
ca_key: Optional CA private key. If provided, ca_cert must also be provided.
21-
ca_cert: Optional CA certificate. If provided, ca_key must also be provided.
22+
ca_key: The CA private key (must be EllipticCurve)
23+
ca_cert: The CA certificate
2224
"""
2325

2426
def __init__(
2527
self,
26-
ca_key: Optional[ec.EllipticCurvePrivateKey] = None,
27-
ca_cert: Optional[x509.Certificate] = None,
28+
ca_key: ec.EllipticCurvePrivateKey,
29+
ca_cert: x509.Certificate,
2830
):
29-
if (ca_key is None) != (ca_cert is None):
30-
raise ValueError("Both ca_key and ca_cert must be provided together, or neither")
31-
32-
if ca_key is not None and ca_cert is not None:
33-
self._ca = (ca_key, ca_cert)
34-
else:
35-
self._ca = self._generate_ca()
31+
"""
32+
Initialize TLSStore with an existing CA key and certificate.
3633
34+
Args:
35+
ca_key: The CA private key (must be EllipticCurve)
36+
ca_cert: The CA certificate
37+
"""
38+
self._ca = (ca_key, ca_cert)
3739
self._store = {}
3840

39-
def _generate_ca(self):
41+
@classmethod
42+
def generate_ca(
43+
cls,
44+
country: str,
45+
state: str,
46+
locality: str,
47+
organization: str,
48+
common_name: str,
49+
) -> "TLSStore":
50+
"""
51+
Generate a new CA and create a TLSStore instance with it.
52+
53+
Args:
54+
country: Two-letter country code (e.g., "FR", "US")
55+
state: State or province name (e.g., "Ile-de-France", "California")
56+
locality: City or locality name (e.g., "Paris", "San Francisco")
57+
organization: Organization name (e.g., "My Company")
58+
common_name: Common name for the CA (e.g., "My Company CA")
59+
60+
Returns:
61+
TLSStore instance with the generated CA
62+
"""
4063
root_key = ec.generate_private_key(ec.SECP256R1())
4164
subject = issuer = x509.Name(
4265
[
43-
x509.NameAttribute(NameOID.COUNTRY_NAME, "FR"),
44-
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Ile-de-France"),
45-
x509.NameAttribute(NameOID.LOCALITY_NAME, "Paris"),
46-
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Asyncio HTTPS Proxy"),
47-
x509.NameAttribute(NameOID.COMMON_NAME, "Asyncio HTTPS Proxy CA"),
66+
x509.NameAttribute(NameOID.COUNTRY_NAME, country),
67+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, state),
68+
x509.NameAttribute(NameOID.LOCALITY_NAME, locality),
69+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
70+
x509.NameAttribute(NameOID.COMMON_NAME, common_name),
4871
]
4972
)
5073
root_cert = (
@@ -83,7 +106,8 @@ def _generate_ca(self):
83106
)
84107
.sign(root_key, hashes.SHA256())
85108
)
86-
return (root_key, root_cert)
109+
return cls(ca_key=root_key, ca_cert=root_cert)
110+
87111

88112
def _generate_cert(
89113
self, domain

tests/test_server.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ async def on_client_connected(self):
2323
def tls_store():
2424
from asyncio_https_proxy.tls_store import TLSStore
2525

26-
return TLSStore()
26+
return TLSStore.generate_ca(
27+
country="FR",
28+
state="Ile-de-France",
29+
locality="Paris",
30+
organization="Test Org",
31+
common_name="Test CA"
32+
)
2733

2834

2935
@pytest.fixture

tests/test_tls_store.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212
@pytest.fixture
1313
def tls_store():
1414
"""Create a fresh TLSStore instance for testing"""
15-
return TLSStore()
15+
return TLSStore.generate_ca(
16+
country="FR",
17+
state="Ile-de-France",
18+
locality="Paris",
19+
organization="Asyncio HTTPS Proxy",
20+
common_name="Asyncio HTTPS Proxy CA"
21+
)
1622

1723

1824
def test_tls_store_initialization(tls_store):

0 commit comments

Comments
 (0)