acme protocol

2022-08-19 ยท 4 min read

A protocol for automated provisioning of web PKI certificates (certs bound to public domains, endorsed by a web root CA like Let's Encrypt).

why #

Before ACME, web service operators had to manually update their web certs. This error prone and time-consuming process meant that many sites either:

  1. Didn't use HTTPS at all, significantly reducing security.
  2. Used multi-year long cert expirations, increasing the danger when a cert was compromised.
  3. Forgot to renew their certs when they expired, leading to outages.

ACME automates and simplifies the process of procuring and renewing web certs so everyone can enjoy HTTPS!

Since the process is fully automated, web services can autonomously renew their web certs without operator intervention, allowing significantly shorter cert expiration times (think weeks/months not years).

ACME is becoming a de-facto requirement as browser vendors reduce the maximum allowable cert expiration time to only a few months.

registration #

Before requesting any certs, a client needs to register an account <keypair> at the CA.

  1. Client generates an account <keypair> for their account at the CA.
  2. Client makes register request to CA containing <pubkey> of <keypair>.
  3. CA responds with proof-of-ownership of <keypair> challenge, which includes a <nonce>.
  4. Client responds with <nonce> signed using <keypair>.

For more details, see

ordering / renewing cert #

  • In order to automate web PKI certificate issuance, CAs need clients to prove ownership over a domain name before they're willing to issue a new child cert for the client.
  • These certs are called Domain Validated (DV) certs, since the CA just validates domain ownership before issuing the cert.

At a high level, the cert ordering process looks like:

  1. Client sends certificate order request for <domain>. The order status is now pending.
  2. CA responds with a random <token> as a proof-of-ownership challenge for <domain>. Client must bind <token> to <domain> using one of the challenge protocols.
  3. Client sends to the CA which of the different challenge types it wishes to prove.
  4. Client and CA perform the challenge protocol. If success, the client's cert order status will transition to ready.
  5. Client sends a signed Certificate Signing Request (CSR) to the CA in order to finalize the order. The CSR is signed using the account <keypair> registered earlier.

http-01 challenge #

  1. Client listens on http port 80. Public DNS should resolve the <domain> to the client's webserver.
  2. Client's webserver should respond with <token>.<jwk-thumbprint> for a GET http://<domain>/.well-known/acme-challenge/<token> request.
    • TODO: what is the <jwk-thumbprint> precisely?
  3. Out-of-band, the CA requests the <token> from the client's URL and verifies that it matches the token it originally requested.

dns-01 challenge #

TODO

tls-alpn-01 challenge #

TODO

registration in detail #

A client creates an account at the ACME server by first requesting a new <nonce>:

HEAD /acme/new-nonce HTTP/1.1
Host: example.com

The server returns a response containing a Replay-Nonce header containing the <nonce>:

HTTP/1.1 200 OK
Replay-Nonce: oFvnlFP1wIhRlYS2jTaXbA
Cache-Control: no-store
Link: <https://example.com/acme/directory>;rel="index"

Next, the client sends a JSON POST request to /acme/new-account.

  • protected contains a base64url-encoded JWK describing the client's account <pubkey>.
  • All the fields in the payload field are optional.
POST /acme/new-account HTTP/1.1
Host: example.com
Content-Type: application/jose+json

{
 "protected": base64url({
   "alg": "ES256",
   "jwk": {...},
   "nonce": "6S8IqOGY7eL2lsGoTZYifg",
   "url": "https://example.com/acme/new-account"
 }),
 "payload": base64url({
   "termsOfServiceAgreed": true,
   "contact": [
	 "mailto:cert-admin@example.org",
	 "mailto:admin@example.org"
   ],
   "onlyReturnExisting": false,
   "externalAccountBinding": {},
 }),
 "signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
}

If successful, the ACME server returns something like the following HTTP response:

HTTP/1.1 201 Created
Content-Type: application/json
Replay-Nonce: D8s4D2mLs8Vn-goWuPQeKA
Link: <https://example.com/acme/directory>;rel="index"
Location: https://example.com/acme/acct/evOfKhNU60wg

{
 "status": "valid",

 "contact": [
   "mailto:cert-admin@example.org",
   "mailto:admin@example.org"
 ],

 "orders": "https://example.com/acme/acct/evOfKhNU60wg/orders"
}