Migration Guide

This document will help you migrate your code to the latest acme4j version.

Migration to Version 0.14

All resource locations are now URL objects (previously they were URIs). This should affect your source only in a minimum way, if any.

Migration to Version 0.13

In the acme4j-client module, the org.shredzone.acme4j.util package has been renamed in order to fix a split package in Java 9. This package is meant to be internal, so this change should not break your code. The same named public package in the acme4j-utils module is unchanged.

Migration to Version 0.12

Java support for the IdenTrust certificate that is used by Let’s Encrypt servers was added to JRE 8u101 in July 2016. For this reason, acme4j does not need to use a hardcoded local truststore anymore. It has been disabled in this version, and the standard Java truststore is used instead.

Migration to Version 0.10

Starting with version 0.10, acme4j requires Java 8 or higher. This is also reflected in the API.

The most noticeable change is that the old java.util.Date has been replaced by the new java.time API in the entire project. If you don’t want to migrate your code to the new API, you can use Date.from() and Date.toInstant() to convert between the different date objects, where necessary.

Migration to Version 0.9

Version 0.9 brought many changes to the internal API. However, this is only relevant if you run your own CA and make own extensions to acme4j (e.g. if you implement a proprietary Challenge). If you use acme4j only for retrieving certificates, you should not notice any changes.

There is one exception: Authorization.findCombinations() previously returned null if it did not find a matching set of combinations. Now it returns an empty list instead, to avoid unnecessary null checks in your code. If you use this method, make sure your code correctly handles empty lists.

If you use Authorization.findChallenge(), no changes are necessary to your code.

Migration to Version 0.6

With version 0.6, acme4j underwent a major change to the API.

In previous versions, the resource classes like Registration or Authorization were plain data transport objects, and an AcmeClient was used for the actual server communication. Now, the resource classes communicate directly with the server. The result is an API that is more object oriented.

Instead of an AcmeClient, you need a Session object now. The Session is initialized with the ACME server URI and your account’s key pair.

KeyPair keyPair = ... // your account KeyPair
Session session = new Session("acme://letsencrypt.org/staging", keyPair);

Instead of creating a plain Registration object, you now bind it to the session.

URI accountLocationUri = ... // your account's URI
Registration registration = Registration.bind(session, accountLocationUri);

You must know your account’s location URI. Use a RegistrationBuilder if you do not know it, or if you want to register a new account:

Registration registration;
try {
  // Try to create a new Registration...
  registration = new RegistrationBuilder().create(session);
} catch (AcmeConflictException ex) {
  // It failed because your key was already registered.
  // Retrieve the registration location URI from the exception.
  registration = Registration.bind(session, ex.getLocation());

Let me give an example of how to use the resource objects. To start an authorization process for a domain, we previously needed a Registration object, an Authorization object, and an AcmeClient instance.

This is the old way:

AcmeClient client = ... // your ACME client
Registration registration = ... // your Registration

Authorization auth = new Authorization();

client.newAuthorization(registration, auth);

Now, the Registration object takes care of everything:

Registration registration = ... // your Registration

Authorization auth = registration.authorizeDomain("example.org");

As you can see, the authorization method that actually invokes the ACME server has moved from AcmeClient to Registration.

Let’s continue the example. We find and trigger a HTTP challenge.

Previously, it worked like this:

Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
client.triggerChallenge(registration, challenge);

With the new API, it is more concise now:

Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);

Note that the authorize() method is not needed any more, and has been removed without replacement.

As a rule of thumb, you will find the action methods in one of the objects you previously passed as parameter to the AcmeClient method. For example, when you wrote client.triggerChallenge(registration, challenge), you will find the new trigger method in either registration or challenge (here it’s challenge).

The API has also been cleaned up, with many confusing setter methods being removed. If you should miss a setter method, you was actually not supposed to invoke it anyway.