acme_lib/
lib.rs

1#![warn(clippy::all)]
2//! acme-lib is a library for accessing ACME (Automatic Certificate Management Environment)
3//! services such as [Let's Encrypt](https://letsencrypt.org/).
4//!
5//! Uses ACME v2 to issue/renew certificates.
6//!
7//! # Example
8//!
9//! ```no_run
10//! use acme_lib::{Error, Directory, DirectoryUrl};
11//! use acme_lib::persist::FilePersist;
12//! use acme_lib::create_p384_key;
13//!
14//! fn request_cert() -> Result<(), Error> {
15//!
16//! // Use DirectoryUrl::LetsEncryptStaging for dev/testing.
17//! let url = DirectoryUrl::LetsEncrypt;
18//!
19//! // Save/load keys and certificates to current dir.
20//! let persist = FilePersist::new(".");
21//!
22//! // Create a directory entrypoint.
23//! let dir = Directory::from_url(persist, url)?;
24//!
25//! // Reads the private account key from persistence, or
26//! // creates a new one before accessing the API to establish
27//! // that it's there.
28//! let acc = dir.account("foo@bar.com")?;
29//!
30//! // Order a new TLS certificate for a domain.
31//! let mut ord_new = acc.new_order("mydomain.io", &[])?;
32//!
33//! // If the ownership of the domain(s) have already been
34//! // authorized in a previous order, you might be able to
35//! // skip validation. The ACME API provider decides.
36//! let ord_csr = loop {
37//!     // are we done?
38//!     if let Some(ord_csr) = ord_new.confirm_validations() {
39//!         break ord_csr;
40//!     }
41//!
42//!     // Get the possible authorizations (for a single domain
43//!     // this will only be one element).
44//!     let auths = ord_new.authorizations()?;
45//!
46//!     // For HTTP, the challenge is a text file that needs to
47//!     // be placed in your web server's root:
48//!     //
49//!     // /var/www/.well-known/acme-challenge/<token>
50//!     //
51//!     // The important thing is that it's accessible over the
52//!     // web for the domain(s) you are trying to get a
53//!     // certificate for:
54//!     //
55//!     // http://mydomain.io/.well-known/acme-challenge/<token>
56//!     let chall = auths[0].http_challenge();
57//!
58//!     // The token is the filename.
59//!     let token = chall.http_token();
60//!     let path = format!(".well-known/acme-challenge/{}", token);
61//!
62//!     // The proof is the contents of the file
63//!     let proof = chall.http_proof();
64//!
65//!     // Here you must do "something" to place
66//!     // the file/contents in the correct place.
67//!     // update_my_web_server(&path, &proof);
68//!
69//!     // After the file is accessible from the web,
70//!     // this tells the ACME API to start checking the
71//!     // existence of the proof.
72//!     //
73//!     // The order at ACME will change status to either
74//!     // confirm ownership of the domain, or fail due to the
75//!     // not finding the proof. To see the change, we poll
76//!     // the API with 5000 milliseconds wait between.
77//!     chall.validate(5000)?;
78//!
79//!     // Update the state against the ACME API.
80//!     ord_new.refresh()?;
81//! };
82//!
83//! // Ownership is proven. Create a private key for
84//! // the certificate. These are provided for convenience, you
85//! // can provide your own keypair instead if you want.
86//! let pkey_pri = create_p384_key();
87//!
88//! // Submit the CSR. This causes the ACME provider to enter a
89//! // state of "processing" that must be polled until the
90//! // certificate is either issued or rejected. Again we poll
91//! // for the status change.
92//! let ord_cert =
93//!     ord_csr.finalize_pkey(pkey_pri, 5000)?;
94//!
95//! // Now download the certificate. Also stores the cert in
96//! // the persistence.
97//! let cert = ord_cert.download_and_save_cert()?;
98//!
99//! Ok(())
100//! }
101//! ```
102//!
103//! ## Domain ownership
104//!
105//! Most website TLS certificates tries to prove ownership/control over the domain they
106//! are issued for. For ACME, this means proving you control either a web server answering
107//! HTTP requests to the domain, or the DNS server answering name lookups against the domain.
108//!
109//! To use this library, there are points in the flow where you would need to modify either
110//! the web server or DNS server before progressing to get the certificate.
111//!
112//! See [`http_challenge`] and [`dns_challenge`].
113//!
114//! ### Multiple domains
115//!
116//! When creating a new order, it's possible to provide multiple alt-names that will also
117//! be part of the certificate. The ACME API requires you to prove ownership of each such
118//! domain. See [`authorizations`].
119//!
120//! [`http_challenge`]: https://docs.rs/acme-lib/latest/acme_lib/order/struct.Auth.html#method.http_challenge
121//! [`dns_challenge`]: https://docs.rs/acme-lib/latest/acme_lib/order/struct.Auth.html#method.dns_challenge
122//! [`authorizations`]: https://docs.rs/acme-lib/latest/acme_lib/order/struct.NewOrder.html#method.authorizations
123//!
124//! ## Rate limits
125//!
126//! The ACME API provider Let's Encrypt uses [rate limits] to ensure the API i not being
127//! abused. It might be tempting to put the `delay_millis` really low in some of this
128//! libraries' polling calls, but balance this against the real risk of having access
129//! cut off.
130//!
131//! [rate limits]: https://letsencrypt.org/docs/rate-limits/
132//!
133//! ### Use staging for dev!
134//!
135//! Especially take care to use the Let`s Encrypt staging environment for development
136//! where the rate limits are more relaxed.
137//!
138//! See [`DirectoryUrl::LetsEncryptStaging`].
139//!
140//! [`DirectoryUrl::LetsEncryptStaging`]: enum.DirectoryUrl.html#variant.LetsEncryptStaging
141//!
142//! ## Implementation details
143//!
144//! The library tries to pull in as few dependencies as possible. (For now) that means using
145//! synchronous I/O and blocking cals. This doesn't rule out a futures based version later.
146//!
147//! It is written by following the
148//! [ACME draft spec 18](https://tools.ietf.org/html/draft-ietf-acme-acme-18), and relies
149//! heavily on the [openssl](https://docs.rs/openssl/) crate to make JWK/JWT and sign requests
150//! to the API.
151//!
152#[macro_use]
153extern crate log;
154
155mod acc;
156mod cert;
157mod dir;
158mod error;
159mod jwt;
160mod req;
161mod trans;
162mod util;
163
164pub mod api;
165pub mod order;
166pub mod persist;
167
168#[cfg(test)]
169mod test;
170
171pub use crate::acc::{Account, RevocationReason};
172pub use crate::cert::{create_p256_key, create_p384_key, create_rsa_key, Certificate};
173pub use crate::dir::{Directory, DirectoryUrl};
174pub use crate::error::{Error, Result};