Automated Certificate Management Environment (ACME) Extensions for ".onion" Special-Use Domain Names
Extracted elements (28)
The 'dns' identifier type is reused for '.onion' domains rather than defining a new identifier type because existing ACME tooling (e.g., Certbot with NGINX) can remain unaware of '.onion' special handling when placed behind a Tor-terminating proxy, requiring no modification. This backward compatibility was a key design driver.
The onion-csr-01 challenge was created because http-01 and tls-alpn-01 do not allow issuance of wildcard certificates, but '.onion' domains can have subdomains and operators may need a single certificate for all virtual hosts. Incorporating the CA/Browser Forum's specially-signed CSR method into ACME enables wildcard issuance.
The http-01 and tls-alpn-01 challenges MAY be used to validate '.onion' domains with the modifications in this document (Sections 4 and 6). The ACME server SHOULD follow redirects, including to non-'.onion' services, to support centralized certificate management servers.
A CA offering certificates to '.onion' Special-Use Domain Names SHOULD make their ACME server available as a Tor Hidden Service. ACME clients SHOULD support connecting to ACME servers over Tor regardless of their support of onion-csr-01.
All subdomains under a '.onion' Special-Use Domain Name share the same CAA record set as the apex, since subdomain delegation is impossible. Implementors MUST NOT look for CAA records at the 'onion' Special-Use TLD as it does not exist in any form except as described in RFC 7686.
For Hidden Services with client authentication enabled, an ACME server MUST wait until the client responds to an authorization before checking CAA, treating the response as confirmation that the server's public key has been added and the Second Layer Hidden Service Descriptor can be decrypted.
If an ACME server requires in-band CAA but the client does not provide an 'onionCAA' object in its finalize request, the server MUST respond with an 'onionCAARequired' error. The 'expiry' field in the onionCAA object SHOULD NOT be more than 8 hours in the future.
The 'dns' identifier type MUST be used when requesting a certificate for a '.onion' Special-Use Domain Name. The identifier value MUST be the textual representation as defined in the Tor spec, and MAY include subdomain labels. Version 2 addresses MUST NOT be used as they are considered insecure.
The existing 'dns-01' challenge MUST NOT be used to validate '.onion' Special-Use Domain Names because these domains are not part of the DNS infrastructure.
The onion-csr-01 challenge nonce MUST contain at least 64 bits of entropy. A response generated using a nonce MUST NOT be accepted by the ACME server if the nonce was generated more than 30 days prior, per CA/Browser Forum Baseline Requirements.
The public key presented in the onion-csr-01 CSR MUST be the public key corresponding to the '.onion' Special-Use Domain Name being validated, and MUST NOT be the same public key presented in the CSR to finalize the order. CAs MUST NOT validate the CSR subject's contents.
When an ACME server encounters a novel Ed25519 authKey, the client must resign and republish its Hidden Service Descriptor and wait for propagation. ACME servers MUST NOT expire challenges before a reasonable time; it is RECOMMENDED the server allow at least 30 minutes for descriptor propagation.
ACME clients requesting certificates for '.onion' domains over a non-Tor connection can inadvertently expose the existence of the Hidden Service, even with ECH, because ACME server IP addresses are well-known. ACME clients SHOULD connect to ACME servers over Tor, preferring a Hidden Service endpoint.
An http-01 redirect to a public DNS domain during validation reveals the target site's IP address and other information to the ACME server operator that Tor was designed to protect. If an ACME server receives such a redirect, it MUST NOT use Tor to connect to the redirected destination. Hidden Service operators MUST consider these privacy implications.
If a Hidden Service operator does not want different Hidden Services correlated by a CA, they MUST use separate ACME account keys for each Hidden Service. Operators SHOULD also provide minimal or obfuscated subscriber information when registering with an ACME server.
If an ACME client requests a publicly trusted WebPKI certificate, the Hidden Service's existence is exposed publicly via Certificate Transparency logs. Hidden Service operators MUST consider the CT privacy implications before requesting WebPKI certificates; ACME client developers SHOULD warn users about this risk.
A 'caa-critical' flag in the First Layer Hidden Service Descriptor signals that CAA records are present in the Second Layer Descriptor. If an ACME server encounters this flag, it MUST NOT proceed with issuance until it can decrypt and parse the CAA records, preventing mis-issuance by CAs unaware of the CAA policy.
The authKey field in ACME challenge objects allows the ACME server to advertise its Ed25519 public key (encoded per RFC 8037) that it will use to authenticate itself when retrieving the Hidden Service Descriptor for services that use client authentication. ACME servers MUST NOT use the same public key with multiple Hidden Services.
The onion-csr-01 challenge is a new ACME challenge type that allows validation of '.onion' Special-Use Domain Names by proving control over the hidden service's private key via a specially crafted CSR. It supports wildcard certificate issuance, unlike http-01 and tls-alpn-01. It MUST NOT be used to issue certificates for Special-Use Domain Names that are not '.onion'.
One new entry added to the ACME Directory Metadata Fields registry: field name 'inBandOnionCAARequired' of type boolean. When true, the ACME server requires the client to provide the CAA record set in the finalize request.
One new entry added to the ACME Error Types registry: type 'onionCAARequired', indicating the CA only supports checking CAA for Hidden Services in-band but the client did not provide an in-band CAA record set.
One new entry added to the ACME Validation Methods registry (defined in RFC 8555 Section 9.7.8): label 'onion-csr-01', identifier type 'dns', ACME 'Y', reference RFC 9799.
An ACME server MUST make its own connection to the Hidden Service via the Tor network and MUST NOT outsource this to a third-party service such as Tor2Web. Additionally, an ACME server MUST NOT utilize Tor for validation of domains that are not '.onion', due to the risk of exit hijacking.
The onion-csr-01 challenge does not use the key authorization string from RFC 8555 Section 8.1, but this does not weaken authorization integrity. Because the validation channel is ACME itself and ACME requires requests to be signed by the account key, the key authorization is redundant.
The Second Layer Hidden Service Descriptor is signed, encrypted, and encoded with a MAC such that only the holder of the hidden service secret key can manipulate what is published there, giving CAA records in the descriptor equivalent or better security to DNS CAA. In-band CAA from the ACME client is verified using the same hidden service key, so the security model is equivalent.
CAA records for '.onion' domains are embedded in the Second Layer Hidden Service Descriptor using the format: 'caa SP flags SP tag SP value NL', which may appear any number of times. The contents of flags, tag, and value are as per RFC 8659 Section 4.1.1.
The onion-csr-01 challenge CSR must include two OID-identified extension attributes: caSigningNonce (OID cabf 41) containing the server's raw nonce bytes, and applicantSigningNonce (OID cabf 42) containing a client-generated nonce of at least 64 bits of entropy. Both attributes use OCTET STRING syntax with SINGLE VALUE constraint. The CSR is submitted as base64url-encoded DER in the 'csr' field.
The onionCAA field in the ACME finalize request carries an in-band CAA record set per '.onion' domain, with subfields: 'caa' (string or null), 'expiry' (Unix timestamp, MUST be processed as at least 64-bit integer), and 'signature' (base64url Ed25519 signature). The signature covers 'onion-caa|' || expiry || '|' || caa in UTF-8.