acme-client

Build Status License Crates.io docs.rs.io

Easy to use Let's Encrypt compatible ACME client to issue, renew and revoke TLS certificates.

Fork

This fork of a fork is published as acmev02. Readme has not been otherwise updated yet. It upgrades reqwest which gets rid of the openssl 0.9 dep, which was breaking my rust-musl-builder builds.

Contents

Installation

By default acme-client crate comes with a handy CLI. You can install acme-client with: cargo install acme-client or you can download pre-built acme-client binary for Linux in the releases page.

Usage

acme-client is using the OpenSSL library to generate all required keys and certificate signing request. You don't need to run any openssl command. You can use your already generated keys and CSR if you want and you don't need any root access while running acme-client.

acme-client is using simple HTTP validation to pass Let's Encrypt's DNS validation challenge. You need a working HTTP server to host the challenge file.

Sign a certificate

sh acme-client sign -D example.org -P /var/www -k domain.key -o domain.crt

This command will generate a user key, domain key and X509 certificate signing request. It will register a new user account and identify the domain ownership by putting the required challenge token into /var/www/.well-known/acme-challenge/. If everything goes well, it will save the domain private key into domain.key and the signed certificate into domain.crt.

You can also use the --email option to provide a contact adress on registration.

Using your own keys and CSR

You can use your own RSA keys for user registration and domain. For example:

sh acme-client sign \ --user-key user.key \ --domain-key domain.key \ --domain-csr domain.csr \ -P /var/www \ -o domain.crt

This will not generate any key and it will use provided keys to sign the certificate. It will also get domain names from provided CSR file.

Using DNS validation

You can use --dns flag to trigger DNS validation instead of HTTP. This option requires user to generate a TXT record for domain. An example DNS validation:

sh $ acme-client sign --dns -D onur.im -E onur@onur.im \ -k /tmp/onur.im.key -o /tmp/onur.im.crt Please create a TXT record for _acme-challenge.onur.im: fDdTmWl4RMuGqj9acJiTC13hF6dVOZUNm3FujCIz3jc Press enter to continue

Revoking a signed certificate

acme-client can also revoke a signed certificate. You need to use your user key and a signed certificate to revoke.

sh acme-client revoke --user-key user.key --signed-crt signed.crt

Options

You can get a list of all available options with acme-client sign --help and acme-client revoke --help:

``` $ acme-client sign --help acme-client-sign Signs a certificate

USAGE: acme-client sign [FLAGS] [OPTIONS]

FLAGS: -d, --dns Use DNS challenge instead of HTTP. This option requires user to generate a TXT record for domain. -h, --help Prints help information -V, --version Prints version information

OPTIONS: -A, --directory Set ACME directory URL [default: https://acme-v01.api.letsencrypt.org/directory] -D, --domain ... Domain name to obtain certificate. You can use more than one domain name. -P, --public-dir Directory to save ACME simple HTTP challenge. This option is required unless --dns option is being used. -U, --user-key Path to load user private key to use it in account registration. This is optional and acme-client will generate one if it's not supplied. -C, --csr Path to load domain certificate signing request. acme-client can also use CSR to get domain names. This is optional and acme-client will generate one if it's not supplied. -K, --domain-key Path to load private domain key. This is optional and acme-client will generate one if it's not supplied. -E, --email Contact email address (optional). -c, --save-chained-crt Chain signed certificate with Let's Encrypt Authority X3 (IdenTrust cross-signed) intermediate certificate and save to given path. -r, --save-csr Path to save domain certificate signing request generated by acme-client. -k, --save-domain-key Path to save domain private key generated by acme-client. -i, --save-intermediate-crt Path to save intermediate certificate. -o, --save-crt Path to save signed certificate. Default is STDOUT. -u, --save-user-key Path to save private user key. ```

``` $ acme-client revoke --help acme-client-revoke Revokes a signed certificate

USAGE: acme-client revoke --user-key --signed-crt

FLAGS: -h, --help Prints help information -V, --version Prints version information

OPTIONS: -C, --signed-crt Path to signed domain certificate to revoke. -K, --user-key User or domain private key path. ```

There is also genkey and gencsr subcommands to generate RSA private key and CSR. You can use multiple -v flags for verbose output.

Library

You can read entire API documentation in docs.rs. You can use acme-client library by adding following lines to your Cargo.toml:

toml [dependencies] acme-client = "0.5"

By default acme-client will build CLI. You can disable this with:

toml [dependencies.acme-client] version = "0.5" default-features = false

API overview

To successfully sign a SSL certificate for a domain name, you need to identify ownership of your domain. You can also identify and sign certificate for multiple domain names and explicitly use your own private keys and certificate signing request (CSR), otherwise this library will generate them. Basic usage of acme-client:

```rust,norun use acmeclient::Directory;

let directory = Directory::letsencrypt()?; let account = directory.accountregistration().register()?;

// Create a identifier authorization for example.com let authorization = account.authorization("example.com")?;

// Validate ownership of example.com with http challenge let httpchallenge = authorization.gethttpchallenge().okor("HTTP challenge not found")?; httpchallenge.savekeyauthorization("/var/www")?; httpchallenge.validate()?;

let cert = account.certificatesigner(&["example.com"]).signcertificate()?; cert.savesignedcertificate("certificate.pem")?; cert.saveprivatekey("certificate.key")?; ```

acme-client supports signing a certificate for multiple domain names with SAN. You need to validate ownership of each domain name:

```rust,norun use acmeclient::Directory;

let directory = Directory::letsencrypt()?; let account = directory.accountregistration().register()?;

let domains = ["example.com", "example.org"];

for domain in domains.iter() { let authorization = account.authorization(domain)?; // ... }

let cert = account.certificatesigner(&domains).signcertificate()?; cert.savesignedcertificate("certificate.pem")?; cert.saveprivatekey("certificate.key")?; ```

Account registration

```rust,norun use acmeclient::Directory;

let directory = Directory::letsencrypt()?; let account = directory.accountregistration() .email("example@example.org") .register()?; ```

Contact email address is optional. You can also use your own private key during registration. See AccountRegistration helper for more details.

If you already registed with your own keys before, you still need to use register method, in this case it will identify your user account instead of creating a new one.

Identifying ownership of domain name

Before sending a certificate signing request to an ACME server, you need to identify ownership of domain names you want to sign a certificate for. To do that you need to create an Authorization object for a domain name and fulfill at least one challenge (http or dns for Let's Encrypt).

To create an Authorization object for a domain:

rust,no_run let authorization = account.authorization("example.com")?;

Authorization object will contain challenges created by ACME server. You can create as many Authorization object as you want to verifiy ownership of the domain names. For example if you want to sign a certificate for example.com and example.org:

rust,no_run let domains = ["example.com", "example.org"]; for domain in domains.iter() { let authorization = account.authorization(domain)?; // ... }

Identifier validation challenges

When you send authorization request to an ACME server, it will generate identifier validation challenges to provide assurence that an account holder is also the entity that controls an identifier.

HTTP challenge

With HTTP validation, the client in an ACME transaction proves its control over a domain name by proving that it can provision resources on an HTTP server that responds for that domain name.

acme-client has save_key_authorization method to save vaditation file to a public directory. This directory must be accessible to outside world.

```rust,norun let authorization = account.authorization("example.com")?; let httpchallenge = authorization.gethttpchallenge().ok_or("HTTP challenge not found")?;

// This method will save key authorization into // /var/www/.well-known/acme-challenge/ directory. httpchallenge.savekey_authorization("/var/www")?;

// Validate ownership of example.com with http challenge http_challenge.validate()?; ```

During validation, ACME server will check http://example.com/.well-known/acme-challenge/{token} to identify ownership of domain name. You need to make sure token is publicly accessible.

DNS challenge:

The DNS challenge requires the client to provision a TXT record containing a designated value under a specific validation domain name.

acme-client can generated this value with signature method.

The user constructs the validation domain name by prepending the label "_acme-challenge" to the domain name being validated, then provisions a TXT record with the digest value under that name. For example, if the domain name being validated is "example.com", then the client would provision the following DNS record:

text _acme-challenge.example.com: dns_challenge.signature()

Example validation with DNS challenge:

```rust,norun let authorization = account.authorization("example.com")?; let dnschallenge = authorization.getdnschallenge().okor("DNS challenge not found")?; let signature = dnschallenge.signature()?;

// User creates a TXT record for _acme-challenge.example.com with the value of signature.

// Validate ownership of example.com with DNS challenge dns_challenge.validate()?; ```

Signing a certificate

After validating all the domain names you can send a sign certificate request. acme-client provides CertificateSigner helper for this. You can use your own key and CSR or you can let CertificateSigner to generate them for you.

```rust,no_run let domains = ["example.com", "example.org"];

// ... validate ownership of domain names

let certificatesigner = account.certificatesigner(&domains); let cert = certificatesigner.signcertificate()?; cert.savesignedcertificate("certificate.pem")?; cert.saveprivatekey("certificate.key")?; ```

Revoking a signed certificate

You can use revoke_certificate or revoke_certificate_from_file methods to revoke a signed certificate. You need to register with the same private key you registered before to successfully revoke a signed certificate. You can also use private key used to generate CSR.

rust,no_run let account = directory.account_registration() .pkey_from_file("user.key")? .register()?; account.revoke_certificate_from_file("certificate.pem")?;

References