Advanced Topics

Change of TOS

If the CA changes the terms of service and requires an explicit agreement to the new terms, an AcmeUserActionRequiredException will be thrown. Its getInstance() method returns the URL of a human-readable web document that gives instructions about how to agree to the new terms of service (e.g. by clicking on a confirmation button).

Unfortunately, the AcmeUserActionRequiredException can be thrown at any time acme4j is contacting the CA, and won't go away by itself.

There is no way to automatize this process. It requires human interaction, even on a Saturday night. Note that this is a limitation of the ACME protocol, not acme4j.

Custom CSR

Usually acme4j takes the hassle of creating a simple CSR for you. If you need more control over the CSR file, you can provide a PKCS#10 CSR file, either as PKCS10CertificationRequest instance or as DER formatted binary. The CSR must provide exactly the domains that you had passed to the order(), otherwise the finalization will fail on server side.

To create a CSR, you can use command like tools like openssl or Java frameworks like Bouncy Castle.

For your convenience, there is a CSRBuilder that simplifies the CSR generation and should be sufficient for most use cases.

KeyPair domainKeyPair = ... // KeyPair to be used for HTTPS encryption

CSRBuilder csrb = new CSRBuilder();
csrb.setOrganization("The Example Organization")

csrb.write(new FileWriter("example.csr"));  // Write to file

byte[] csr = csrb.getEncoded();  // Get a binary representation

The CSRBuilder also accepts IP addresses and Identifier for generating the CSR:

CSRBuilder csrb = new CSRBuilder();

The CSRBuilder is used internally for creating the CSR, and you can take influence on the generated CSR by using the Order.execute(KeyPair domainKeyPair, Consumer<CSRBuilder> builderConsumer) method.

Domain Pre-Authorization

It is possible to pro-actively authorize a domain, without ordering a certificate yet. This can be useful to find out what challenges are requested by the CA to authorize a domain. It may also help to speed up the ordering process, as already completed authorizations do not need to be completed again when ordering the certificate in the near future.

Account account = ... // your Account object
String domain = ...   // Domain name to authorize

Authorization auth = account.preAuthorize(Identifier.dns(domain));


Some CAs may not offer domain pre-authorization, preAuthorizeDomain() will then fail and throw an AcmeNotSupportedException. Some CAs may limit pre-authorization to certain domain types (e.g. non-wildcard) and throw an AcmeServerException otherwise.

To pre-authorize a domain for subdomain certificates as specified in RFC 9444, flag the Identifier accordingly using allowSubdomainAuth():

Account account = ... // your Account object
String domain = ...   // Domain name to authorize

Authorization auth = account.preAuthorize(Identifier.dns(domain).allowSubdomainAuth());

Localized Error Messages

By default, acme4j will send your system's default locale as Accept-Language header to the CA (with a fallback to any other language). If the language is supported by the CA, it will return localized error messages.

To select another language, use Session.setLocale(). The change will only affect that session, so you can have multiple sessions with different locale settings.

Network Settings

You can use Session.networkSettings() to change some network parameters for the session.

  • If a proxy must be used for internet connections, you can set a ProxySelector instance via setProxySelector().
  • To change network timeouts, use setTimeout(). The default timeout is 10 seconds. You can either increase the timeout for poor network connections, or reduce it to fail early on network errors. The change affects connection and read timeouts.
  • If you need authentication (e.g. for the proxy), you can set an Authenticator via setAuthenticator(). Be careful here! Most code snippets I have found on the internet will send out the full proxy credentials to anyone who is asking. You should check Authenticator.getRequestorType() and make sure it is RequestorType.PROXY before sending the proxy credentials.
  • acme4j accepts HTTP gzip compression by default. If it should impede debugging, it can be disabled via setCompressionEnabled(false).