While Boulder attempts to implement the ACME specification as strictly as possible there are places at which we will diverge from the letter of the specification for various reasons.
Since Boulder evolved alongside the ACME specification there is not one exact ACME draft number that can be referenced in isolation to understand the protocol Boulder speaks. This document exists to detail differences between what Boulder does and the most-recently published ACME draft. Since ACME is not yet finalized it will be updated as new numbered drafts are published.
ACME v2 divergences from draft-ietf-acme-acme-15
.
Presently the following protocol features are not implemented:
- Pre-authorization. This is an optional feature and we have no plans to implement it. V2 clients should use order based issuance without pre-authorization.
- The
orders
field on account objects. We intend to support this non-essential feature in the near future. Please follow Boulder Issue #3335. - POST-as-GET. We currently allow unauthenticated GET requests to orders, authorizations, challenges and certificates. We intend to implement support for POST-as-GET before gradually deprecating unauthenticated GET requests. Please follow Boulder Issue #3871.
ACME v1 divergences from draft-ietf-acme-acme-07
.
Boulder does not implement the general JWS syntax, but only accepts the flattened syntax.
Boulder enforces the presence of the jwk
field in JWS objects, and does not support the kid
field.
Boulder does not use the url
field from the JWS protected resource. Instead Boulder will validate the resource
field from the JWS payload matches the resource being requested. Boulder implements the resource types described in draft-ietf-acme-02 Section 6.1 plus the additional "KeyChange" resource. Boulder verifies the resource
field contains the /directory
URI for the requested resource.
Boulder does not provide a Retry-After
header when a user hits a rate-limit, nor does it provide Link
headers to further documentation on rate-limiting.
Boulder doesn't return errors under the urn:ietf:params:acme:error:
namespace but instead uses the urn:acme:error:
namespace from draft-ietf-acme-01 Section 5.4.
Boulder uses invalidEmail
in place of the error invalidContact
defined in draft-ietf-acme-01 Section 5.4.
Boulder does not implement the unsupportedContact
and accountDoesNotExist
errors.
Boulder does not implement the caa
and dnssec
errors.
Boulder does not implement the new-order
resource (previously referred to as new-application
). Instead of new-order
Boulder implements the new-cert
resource that is defined in draft-ietf-acme-02 Section 6.5.
Boulder also doesn't implement the new-nonce
endpoint.
Boulder implements the new-account
ressource only under the new-reg
key.
Boulder implements Link: rel="next" headers from new-reg to new-authz, and new-authz to new-cert, as specified in draft-02, but these links are not provided in the latest draft, and clients should use URLs from the directory instead.
Boulder does not provide the "index" link relation pointing at the directory URL.
Boulder does not implement the terms-of-service-agreed
or orders
fields in the registration object (nor the endpoints the latter links to).
Boulder does not implement orders (previously called applications
), instead it implements the new-cert
flow from draft-ietf-acme-02 Section 6.5. Instead of authorizations in the order response, Boulder currently uses authorizations that are created using the new-authz
flow from draft-ietf-acme-02 Section 6.4.
Boulder does not implement the scope
field in authorization objects.
Boulder doesn't implement the new-nonce
endpoint, instead it responds to HEAD
requests with a valid Replay-Nonce
header per draft-ietf-acme-03 Section 5.4.
Boulder only allows mailto
URIs in the registrations contact
list.
Boulder uses an HTTP status code 409 (Conflict) response for an already existing registration instead of 200 (OK). Boulder returns the URI of the already existing registration in a Location
header field instead of a Content-Location
header field.
Boulder does not return the status
field.
Boulder does not implement the only-return-existing
field.
Boulder does not implement the only-return-existing
behaviour and will always create a new account if an account for the given key does not exist.
Boulder implements draft-05 style key roll-over with a few divergences. Since Boulder doesn't currently use the registration URL to identify users we do not check for that field in the JWS protected headers but do check for it in the inner payload. Boulder also requires the outer JWS payload contains the "resource": "key-change"
field.
Boulder does not implement orders (previously called applications
), instead it implements the new-cert
flow from draft-ietf-acme-02 Section 6.5. Instead of authorizations in the order response, Boulder currently uses authorizations that are created using the new-authz
flow from draft-ietf-acme-02 Section 6.4. Certificates are not proactively issued, a user must request issuance via the new-cert
endpoint instead of assuming a certificate will be created once all required authorizations are validated.
Boulder does not process Accept
headers for Content-Type
negotiation when retrieving certificates. Boulder returns certificates with the Content-Type
value application/pkix-cert
instead of application/pem-certificate-chain
.
Boulder returns an uri
instead of an url
field in challenge objects.
Boulder uses an HTTP status code 202 (Accepted) response for correct challenge responses instead of 200 (OK) as defined in Section 7.1.
Boulder does not implement the ability to retry challenges or the Retry-After
header.
Boulder does not implement the oob-01
validation method.
Boulder uses the urn:acme:
namespace from draft-ietf-acme-01 Section 5.4 for errors instead of urn:ietf:params:acme:
.