Async ACME client implementation
Project description
aioacme
Simple async client for ACME protocol.
Installation
pip install aioacme
Usage
Issue certificate with DNS-01 challenge:
import asyncio
import aioacme
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa
async def main():
account_key = serialization.load_pem_private_key(...)
async with aioacme.Client(
account_key=account_key,
directory_url=aioacme.LETS_ENCRYPT_STAGING_DIRECTORY
) as client:
order = await client.new_order([aioacme.Identifier("example.com")])
for authorization_uri in order.authorizations:
authorization = await client.get_authorization(authorization_uri)
if authorization.status is aioacme.AuthorizationStatus.valid:
# authorization reused
continue
challenge = next(
c for c in authorization.challenges if c.type is aioacme.ChallengeType.dns01
)
domain = client.get_dns_challenge_domain(authorization.identifier.value)
validation = client.get_dns_challenge_validation(challenge.token)
print(f"Please add TXT record {domain} with the following content: {validation}")
input("Press Enter when TXT record is created")
await client.answer_challenge(challenge.url)
while authorization.status not in (
aioacme.AuthorizationStatus.valid,
aioacme.AuthorizationStatus.invalid
):
await asyncio.sleep(1)
authorization = await client.get_authorization(authorization_uri)
if authorization.status is aioacme.AuthorizationStatus.invalid:
raise Exception(f"Authorization status is invalid: {authorization}")
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
csr = (
x509.CertificateSigningRequestBuilder()
.subject_name(x509.Name([
x509.NameAttribute(x509.NameOID.COMMON_NAME, "example.com")
]))
.add_extension(x509.SubjectAlternativeName([
x509.DNSName("example.com")
]), critical=False)
.sign(key, hashes.SHA256())
)
order = await client.finalize_order(order.finalize, csr)
while order.status not in {aioacme.OrderStatus.valid, aioacme.OrderStatus.invalid}:
await asyncio.sleep(1)
order = await client.get_order(order.uri)
if order.status is aioacme.OrderStatus.invalid:
raise Exception(f"Order status is invalid: {order}")
cert = await client.get_certificate(order.certificate)
print(cert.decode("ascii"))
asyncio.run(main())
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
aioacme-0.3.0.tar.gz
(20.4 kB
view details)
Built Distribution
aioacme-0.3.0-py3-none-any.whl
(14.0 kB
view details)
File details
Details for the file aioacme-0.3.0.tar.gz
.
File metadata
- Download URL: aioacme-0.3.0.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.0.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c277a91860ff8628f5a14b0176d90d318314eb2d9282cbe6aba5f49b12d52065 |
|
MD5 | 721d05d8b98be83eb522512aaecdad9d |
|
BLAKE2b-256 | f4e198a8f3d1b145926db6c3965de7a6f2bc34f8b33be5dc1f3245d9e5f0b64e |
File details
Details for the file aioacme-0.3.0-py3-none-any.whl
.
File metadata
- Download URL: aioacme-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.0.0 CPython/3.12.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5260ab7870834b5fbb0f990465799e3c0bbc9dbd3c10b9d1a7379f9041f67961 |
|
MD5 | e0bc5bea0f6206ee8f8e33a91f753434 |
|
BLAKE2b-256 | 21a7e61db3343e87bda5f5196b0aaefbf9dca1ac746928f659d1b62f142b37b2 |