acme_lite/
lib.rs

1/*!
2`acme-lite` is a library for provisioning certificates from ACME (Automatic Certificate
3Management Environment) services such as [Let's Encrypt](https://letsencrypt.org/).
4
5It follows the [RFC8555](https://datatracker.ietf.org/doc/html/rfc8555) spec, using ACME v2 to
6issue/renew certificates.
7
8# Example
9
10```no_run
11use std::time::Duration;
12
13use acme_lite::{Certificate, Directory, DirectoryUrl, create_p384_key};
14
15# #[tokio::test]
16async fn request_cert() -> eyre::Result<Certificate> {
17    // Use `DirectoryUrl::LetsEncrypt` for production.
18    let url = DirectoryUrl::LetsEncryptStaging;
19
20    // Create a directory entrypoint.
21    let dir = Directory::from_url(url).await?;
22
23    // Your contact addresses, note the `mailto:`
24    let contact = vec!["mailto:foo@bar.com".to_owned()];
25
26    // Generate a private key and register an account with your ACME provider.
27    // You should write it to disk any use `load_account` afterwards.
28    let acc = dir.register_account(Some(contact.clone()))?;
29
30    // Example of how to load an account from string:
31    let singing_key = acc.acme_private_key_pem()?;
32    let acc = dir.load_account(&singing_key, Some(contact))?;
33
34    // Order a new TLS certificate for a domain.
35    let mut ord_new = acc.new_order("example.org", &[])?;
36
37    // If the ownership of the domain(s) have already been
38    // authorized in a previous order, you might be able to
39    // skip validation. The ACME API provider decides.
40    let ord_csr = loop {
41        // are we done?
42        if let Some(ord_csr) = ord_new.confirm_validations() {
43            break ord_csr;
44        }
45
46        // Get the possible authorizations (for a single domain
47        // this will only be one element).
48        let auths = ord_new.authorizations()?;
49
50        // For HTTP, the challenge is a text file that needs to
51        // be placed in your web server's root:
52        //
53        // /var/www/.well-known/acme-challenge/<token>
54        //
55        // The important thing is that it's accessible over the
56        // web for the domain(s) you are trying to get a
57        // certificate for:
58        //
59        // http://example.org/.well-known/acme-challenge/<token>
60        let challenge = auths[0].http_challenge().unwrap();
61
62        // The token is the filename.
63        let token = challenge.http_token();
64        let path = format!(".well-known/acme-challenge/{token}");
65
66        // The proof is the contents of the file
67        let proof = challenge.http_proof()?;
68
69        // Here you must do "something" to place
70        // the file/contents in the correct place.
71        // update_my_web_server(&path, &proof);
72
73        // After the file is accessible from the web, this tells the ACME API
74        // to start checking the existence of the proof.
75        //
76        // The order at ACME will change status to either
77        // confirm ownership of the domain, or fail due to the
78        // not finding the proof. To see the change, we poll
79        // the API with 5000 milliseconds wait between.
80        challenge.validate(Duration::from_millis(5000))?;
81
82        // Update the state against the ACME API.
83        ord_new.refresh()?;
84    };
85
86    // Ownership is proven. Create a private key for
87    // the certificate. These are provided for convenience, you
88    // can provide your own keypair instead if you want.
89    let signing_key = create_p256_key();
90
91    // Submit the CSR. This causes the ACME provider to enter a
92    // state of "processing" that must be polled until the
93    // certificate is either issued or rejected. Again we poll
94    // for the status change.
95    let ord_cert = ord_csr.finalize_signing_key(signing_key, Duration::from_millis(5000)).await?;
96
97    // Now download the certificate. Also stores the cert in
98    // the persistence.
99    let cert = ord_cert.download_cert().await?;
100    println!("{cert:?}");
101
102    Ok(cert)
103}
104```
105
106## Domain ownership
107
108Most website TLS certificates tries to prove ownership/control over the domain they
109are issued for. For ACME, this means proving you control either a web server answering
110HTTP requests to the domain, or the DNS server answering name lookups against the domain.
111
112To use this library, there are points in the flow where you would need to modify either
113the web server or DNS server before progressing to get the certificate.
114
115See [`http_challenge`] and [`dns_challenge`].
116
117### Multiple domains
118
119When creating a new order, it's possible to provide multiple alt-names that will also
120be part of the certificate. The ACME API requires you to prove ownership of each such
121domain. See [`authorizations`].
122
123[`http_challenge`]: https://docs.rs/acme-lite/0.1/acme_lite/order/struct.Auth.html#method.http_challenge
124[`dns_challenge`]: https://docs.rs/acme-lite/0.1/acme_lite/order/struct.Auth.html#method.dns_challenge
125[`authorizations`]: https://docs.rs/acme-lite/0.1/acme_lite/order/struct.NewOrder.html#method.authorizations
126
127## Rate limits
128
129The ACME API provider Let's Encrypt uses [rate limits] to ensure the API i not being
130abused. It might be tempting to put the `delay` really low in some of this
131libraries' polling calls, but balance this against the real risk of having access
132cut off.
133
134[rate limits]: https://letsencrypt.org/docs/rate-limits/
135
136### Use staging for development!
137
138Especially take care to use the Let's Encrypt staging environment for development where the rate
139limits are more relaxed. See [`DirectoryUrl::LetsEncryptStaging`].
140*/
141
142#![deny(rust_2018_idioms, nonstandard_style, future_incompatible)]
143
144mod acc;
145mod cert;
146mod dir;
147mod error;
148mod jws;
149mod req;
150mod trans;
151
152pub mod api;
153pub mod order;
154
155#[cfg(test)]
156mod test;
157
158pub use crate::{
159    acc::{Account, RevocationReason},
160    cert::{create_p256_key, create_p384_key, create_rsa_key, Certificate},
161    dir::{Directory, DirectoryUrl},
162};