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

mod acc;
mod cert;
mod dir;
mod error;
mod jwt;
mod req;
mod trans;
mod util;

pub mod api;
pub mod order;
pub mod persist;

#[cfg(test)]
mod test;

pub use crate::acc::{Account, RevocationReason};
pub use crate::cert::{create_p256_key, create_p384_key, create_rsa_key, Certificate};
pub use crate::dir::{Directory, DirectoryUrl};
pub use crate::error::{Error, Result};