Overview of instant-acme
mainTokio for the async runtime, hyper with rustls for HTTP requests, and supports both aws-lc-rs and ring for ECDSA signing.repository·main·Indexed 42 days ago
https://github.com/djc/instant-acmeAn async, pure-Rust ACME (RFC 8555) client for automated TLS certificate provisioning. Version 0.9.0. It utilizes Tokio for the async runtime and hyper with rustls for HTTP requests, supporting crypto backends such as aws-lc-rs and ring for ECDSA signing.
Tokio for the async runtime, hyper with rustls for HTTP requests, and supports both aws-lc-rs and ring for ECDSA signing.When an ACME server returns an error, it follows RFC 7807. The Problem struct captures these details:
r#type: An enumerated problem type (e.g., urn:ietf:params:acme:error:invalid-environment).detail: A human-readable explanation.status: The HTTP status code.subproblems: A list of Subproblem objects, which can associate errors with specific identifiers.The Account struct is the primary interface for managing ACME account operations. It is cheap to clone and provides methods for creating orders, revoking certificates, updating account information, and managing account keys.
To use an Account, you typically start with an AccountBuilder to either create a new account or restore one from existing AccountCredentials.
// Example conceptual flow
let builder = Account::builder(http_client, &crypto_provider)?;
let account = builder.create(&new_account_info, directory_url, None).await?;
// Use the account to create an order
let order = account.new_order(&new_order_payload).await?;CertificateIdentifier is an encoded representation of a certificate used for renewal and replacement. It is constructed from the certificate's Authority Key Identifier (AKI) and its Serial Number. The identifier is serialized as a string (e.g., "authority_key_id.serial").Problem type. A Problem contains a type (a URI identifying the error), a detail string, and an optional list of subproblems. Subproblems allow the CA to provide granular error details, such as which specific identifier in a request caused a failure.You can customize the library's behavior and dependencies using Cargo features.
aws-lc-rs (default): Uses the aws-lc-rs crate as the crypto backend.ring: Uses the ring crate as the crypto backend.aws-lc-rs takes precedence.fips: Enables FIPS-compliant mode for the aws-lc-rs backend.x509-parser: Enables extracting CertificateIdentifier values for ACME Renewal Information (ARI) from certificates.time: Enables fetching RenewalInfo for a CertificateIdentifier.hyper-rustls (default): Uses a hyper client with rustls.A BytesResponse is an object-safe representation of an HTTP response used by the client. It contains:
parts: The http::response::Parts (status code, headers, etc.).body: A boxed BytesBody that can be consumed to retrieve the response payload as Bytes.// To consume the body:
let body_bytes = response.body.into_bytes().await?;The Order struct represents a stable identity for an ACME order (RFC 8555). You can create an order from an Account using Account::new_order().
Key workflows:
order.authorizations() to get a stream of authorizations required to validate the identifiers in the order.AuthorizationHandle::challenge(type) to get a ChallengeHandle, then use that handle to complete the specific challenge (e.g., via set_ready() or key_authorization()).order.poll_ready(retry_policy) to wait for the order to become ready, then call order.finalize() (which generates a CSR and finalizes) or order.finalize_csr(der_bytes) if you have a custom CSR.order.poll_certificate(retry_policy) to wait for and retrieve the PEM encoded certificate chain.// Conceptual workflow
let mut order = account.new_order(identifiers).await?;
// 1. Handle Authorizations/Challenges
let mut auths = order.authorizations();
while let Some(Ok(auth_handle)) = auths.next().await {
let mut challenge = auth_handle.challenge(ChallengeType::Http01)?;
// ... setup challenge response ...
challenge.set_ready().await?;
}
// 2. Wait for readiness and finalize
let retry_policy = RetryPolicy::new();
order.poll_ready(&retry_policy).await?;
let private_key_pem = order.finalize().await?;
// 3. Wait for certificate
let cert_pem = order.poll_certificate(&retry_policy).await?;Use AccountBuilder to initialize an Account instance. There are several ways to instantiate an account:
.create() with NewAccount details and a directory URL..from_credentials() with previously serialized AccountCredentials..from_key() to load an account associated with a specific key, or .create_from_key() to create a new account if one doesn't exist for that key..from_parts() if you have the account ID, a PKCS#8 DER private key, and the directory URL separately.// Create a new account
let (account, credentials) = builder
.create(&new_account, directory_url, None)
.await?;
// Restore an account from stored credentials
let account = builder
.from_credentials(stored_credentials)
.await?;
// Load an account using a specific private key
let (account, credentials) = builder
.from_key(key_tuple, directory_url)
.await?;A ChallengeHandle is used to interact with a specific challenge within an authorization.
To complete a challenge:
handle.key_authorization() to generate the required response value. Depending on the challenge type, use .dns_value() (for DNS-01), .as_str() (for HTTP-01), or .digest() (for TLS-ALPN-01).handle.set_ready().await to tell the ACME server that the challenge is ready for validation.For the experimental device-attest-01 challenge, use handle.send_device_attestation(payload) instead.
// Example: HTTP-01 challenge
let mut challenge = auth_handle.challenge(ChallengeType::Http01)?;
let auth_value = challenge.key_authorization()?.as_str();
// [User logic: Place auth_value in .well-known/acme-challenge/...]
challenge.set_ready().await?;When using instant-acme, be aware of the following current limitations:
The CryptoProvider struct is the central configuration point for cryptographic operations required by the ACME protocol, such as JWS signing, JWK thumbprints, and EAB HMAC.
Note that CryptoProvider is distinct from the rustls::crypto::CryptoProvider used for TLS/HTTPS connections.
You can use built-in backends by enabling the corresponding Cargo features, or implement your own by manually populating the fields:
key_provider: A SigningKeyProvider for loading and generating account keys.sha256: A Sha256 implementation for JWK thumbprints and challenge digests.hmac: An HmacKeyProvider for External Account Binding (EAB).Built-in backends are available via:
CryptoProvider::aws_lc_rs() (requires aws-lc-rs feature)CryptoProvider::ring() (requires ring feature)// Using built-in ring backend
let provider = CryptoProvider::ring();