mirror of https://github.com/shred/acme4j
Upgrade to draft-ietf-acme-ari-03
parent
6a4770c23a
commit
48c32f612d
|
@ -16,7 +16,7 @@ It is an independent open source implementation that is not affiliated with or e
|
||||||
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
|
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
|
||||||
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
|
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
|
||||||
* Supports [RFC 8823](https://tools.ietf.org/html/rfc8823) for S/MIME certificates (experimental)
|
* Supports [RFC 8823](https://tools.ietf.org/html/rfc8823) for S/MIME certificates (experimental)
|
||||||
* Supports [draft-ietf-acme-ari-01](https://www.ietf.org/archive/id/draft-ietf-acme-ari-01.html) for renewal information
|
* Supports [draft-ietf-acme-ari-03](https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html) for renewal information
|
||||||
* Easy to use Java API
|
* Easy to use Java API
|
||||||
* Requires JRE 11 or higher
|
* Requires JRE 11 or higher
|
||||||
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
|
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
|
||||||
|
|
|
@ -16,6 +16,8 @@ package org.shredzone.acme4j;
|
||||||
import static java.util.Collections.unmodifiableList;
|
import static java.util.Collections.unmodifiableList;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.stream.Collectors.toUnmodifiableList;
|
import static java.util.stream.Collectors.toUnmodifiableList;
|
||||||
|
import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode;
|
||||||
|
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
@ -197,7 +199,10 @@ public class Certificate extends AcmeResource {
|
||||||
*
|
*
|
||||||
* @see <a href="https://www.rfc-editor.org/rfc/rfc6960.html">RFC 6960</a>
|
* @see <a href="https://www.rfc-editor.org/rfc/rfc6960.html">RFC 6960</a>
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
|
* @deprecated Is not needed in the ACME context anymore and will thus be removed in
|
||||||
|
* a later version.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getCertID() {
|
public String getCertID() {
|
||||||
var certChain = getCertificateChain();
|
var certChain = getCertificateChain();
|
||||||
if (certChain.size() < 2) {
|
if (certChain.size() < 2) {
|
||||||
|
@ -212,7 +217,7 @@ public class Certificate extends AcmeResource {
|
||||||
var digestCalc = builder.build().get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
|
var digestCalc = builder.build().get(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256));
|
||||||
var issuerHolder = new X509CertificateHolder(certChain.get(1).getEncoded());
|
var issuerHolder = new X509CertificateHolder(certChain.get(1).getEncoded());
|
||||||
var certId = new CertificateID(digestCalc, issuerHolder, certChain.get(0).getSerialNumber());
|
var certId = new CertificateID(digestCalc, issuerHolder, certChain.get(0).getSerialNumber());
|
||||||
return AcmeUtils.base64UrlEncode(certId.toASN1Primitive().getEncoded());
|
return base64UrlEncode(certId.toASN1Primitive().getEncoded());
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
throw new AcmeProtocolException("Could not compute Certificate ID", ex);
|
throw new AcmeProtocolException("Could not compute Certificate ID", ex);
|
||||||
}
|
}
|
||||||
|
@ -236,7 +241,7 @@ public class Certificate extends AcmeResource {
|
||||||
if (!url.endsWith("/")) {
|
if (!url.endsWith("/")) {
|
||||||
url += '/';
|
url += '/';
|
||||||
}
|
}
|
||||||
url += getCertID();
|
url += getRenewalUniqueIdentifier(getCertificate());
|
||||||
return new URL(url);
|
return new URL(url);
|
||||||
} catch (MalformedURLException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
throw new AcmeProtocolException("Invalid RenewalInfo URL", ex);
|
throw new AcmeProtocolException("Invalid RenewalInfo URL", ex);
|
||||||
|
@ -278,28 +283,6 @@ public class Certificate extends AcmeResource {
|
||||||
return renewalInfo;
|
return renewalInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals to the CA that this certificate has been successfully replaced by a newer
|
|
||||||
* one. A revocation of this certificate would not disrupt any ongoing services.
|
|
||||||
*
|
|
||||||
* @draft This method is currently based on an RFC draft. It may be changed or
|
|
||||||
* removed without notice to reflect future changes to the draft. SemVer rules
|
|
||||||
* do not apply here.
|
|
||||||
* @throws AcmeNotSupportedException if the CA does not support renewal information.
|
|
||||||
* @since 3.1.0
|
|
||||||
*/
|
|
||||||
public void markAsReplaced() throws AcmeException {
|
|
||||||
LOG.debug("mark as replaced");
|
|
||||||
var session = getSession();
|
|
||||||
var renewalInfoUrl = session.resourceUrl(Resource.RENEWAL_INFO);
|
|
||||||
try (var conn = session.connect()) {
|
|
||||||
var claims = new JSONBuilder();
|
|
||||||
claims.put("certID", getCertID());
|
|
||||||
claims.put("replaced", true);
|
|
||||||
conn.sendSignedRequest(renewalInfoUrl, claims, getLogin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revokes this certificate.
|
* Revokes this certificate.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,12 +14,16 @@
|
||||||
package org.shredzone.acme4j;
|
package org.shredzone.acme4j;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.shredzone.acme4j.challenge.Challenge;
|
import org.shredzone.acme4j.challenge.Challenge;
|
||||||
|
import org.shredzone.acme4j.connector.Resource;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
@ -145,6 +149,31 @@ public class Login {
|
||||||
return new RenewalInfo(this, requireNonNull(location, "location"));
|
return new RenewalInfo(this, requireNonNull(location, "location"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of an existing {@link RenewalInfo} and binds it to this
|
||||||
|
* login.
|
||||||
|
*
|
||||||
|
* @param certificate
|
||||||
|
* {@link X509Certificate} to get the {@link RenewalInfo} for
|
||||||
|
* @return {@link RenewalInfo} bound to the login
|
||||||
|
* @draft This method is currently based on an RFC draft. It may be changed or removed
|
||||||
|
* without notice to reflect future changes to the draft. SemVer rules do not apply
|
||||||
|
* here.
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public RenewalInfo bindRenewalInfo(X509Certificate certificate) throws AcmeException {
|
||||||
|
try {
|
||||||
|
var url = getSession().resourceUrl(Resource.RENEWAL_INFO).toExternalForm();
|
||||||
|
if (!url.endsWith("/")) {
|
||||||
|
url += '/';
|
||||||
|
}
|
||||||
|
url += getRenewalUniqueIdentifier(certificate);
|
||||||
|
return bindRenewalInfo(new URL(url));
|
||||||
|
} catch (MalformedURLException ex) {
|
||||||
|
throw new AcmeProtocolException("Invalid RenewalInfo URL", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of an existing {@link Challenge} and binds it to this
|
* Creates a new instance of an existing {@link Challenge} and binds it to this
|
||||||
* login. Use this method only if the resulting challenge type is unknown.
|
* login. Use this method only if the resulting challenge type is unknown.
|
||||||
|
|
|
@ -15,11 +15,14 @@ package org.shredzone.acme4j;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
|
import static org.shredzone.acme4j.toolbox.AcmeUtils.getRenewalUniqueIdentifier;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||||
|
@ -44,6 +47,7 @@ public class OrderBuilder {
|
||||||
private final Set<Identifier> identifierSet = new LinkedHashSet<>();
|
private final Set<Identifier> identifierSet = new LinkedHashSet<>();
|
||||||
private @Nullable Instant notBefore;
|
private @Nullable Instant notBefore;
|
||||||
private @Nullable Instant notAfter;
|
private @Nullable Instant notAfter;
|
||||||
|
private @Nullable String replaces;
|
||||||
private boolean autoRenewal;
|
private boolean autoRenewal;
|
||||||
private @Nullable Instant autoRenewalStart;
|
private @Nullable Instant autoRenewalStart;
|
||||||
private @Nullable Instant autoRenewalEnd;
|
private @Nullable Instant autoRenewalEnd;
|
||||||
|
@ -174,6 +178,65 @@ public class OrderBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the CA that the ordered certificate will replace a previously issued
|
||||||
|
* certificate. The certificate is identified by its ARI unique identifier.
|
||||||
|
* <p>
|
||||||
|
* Optional, only supported if the CA provides renewal information. However, in this
|
||||||
|
* case the client <em>should</em> include this field.
|
||||||
|
*
|
||||||
|
* @param uniqueId
|
||||||
|
* Certificate's renewal unique identifier.
|
||||||
|
* @return itself
|
||||||
|
* @draft This method is currently based on an RFC draft. It may be changed or removed
|
||||||
|
* without notice to reflect future changes to the draft. SemVer rules do not apply
|
||||||
|
* here.
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public OrderBuilder replaces(String uniqueId) {
|
||||||
|
autoRenewal();
|
||||||
|
this.replaces = Objects.requireNonNull(uniqueId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the CA that the ordered certificate will replace a previously issued
|
||||||
|
* certificate.
|
||||||
|
* <p>
|
||||||
|
* Optional, only supported if the CA provides renewal information. However, in this
|
||||||
|
* case the client <em>should</em> include this field.
|
||||||
|
*
|
||||||
|
* @param certificate
|
||||||
|
* Certificate to be replaced
|
||||||
|
* @return itself
|
||||||
|
* @draft This method is currently based on an RFC draft. It may be changed or removed
|
||||||
|
* without notice to reflect future changes to the draft. SemVer rules do not apply
|
||||||
|
* here.
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public OrderBuilder replaces(X509Certificate certificate) {
|
||||||
|
return replaces(getRenewalUniqueIdentifier(certificate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the CA that the ordered certificate will replace a previously issued
|
||||||
|
* certificate.
|
||||||
|
* <p>
|
||||||
|
* Optional, only supported if the CA provides renewal information. However, in this
|
||||||
|
* case the client <em>should</em> include this field.
|
||||||
|
*
|
||||||
|
* @param certificate
|
||||||
|
* Certificate to be replaced
|
||||||
|
* @return itself
|
||||||
|
* @draft This method is currently based on an RFC draft. It may be changed or removed
|
||||||
|
* without notice to reflect future changes to the draft. SemVer rules do not apply
|
||||||
|
* here.
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
public OrderBuilder replaces(Certificate certificate) {
|
||||||
|
return replaces(certificate.getCertificate());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the earliest date of validity of the first issued certificate. If not set,
|
* Sets the earliest date of validity of the first issued certificate. If not set,
|
||||||
* the start date is the earliest possible date.
|
* the start date is the earliest possible date.
|
||||||
|
@ -312,6 +375,10 @@ public class OrderBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replaces != null) {
|
||||||
|
claims.put("replaces", replaces);
|
||||||
|
}
|
||||||
|
|
||||||
conn.sendSignedRequest(session.resourceUrl(Resource.NEW_ORDER), claims, login);
|
conn.sendSignedRequest(session.resourceUrl(Resource.NEW_ORDER), claims, login);
|
||||||
|
|
||||||
var order = new Order(login, conn.getLocation());
|
var order = new Order(login, conn.getLocation());
|
||||||
|
|
|
@ -15,7 +15,6 @@ package org.shredzone.acme4j;
|
||||||
|
|
||||||
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
import static org.shredzone.acme4j.toolbox.TestUtils.*;
|
import static org.shredzone.acme4j.toolbox.TestUtils.*;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -37,7 +36,6 @@ import java.util.Optional;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.shredzone.acme4j.connector.Resource;
|
import org.shredzone.acme4j.connector.Resource;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeNotSupportedException;
|
|
||||||
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
||||||
import org.shredzone.acme4j.toolbox.JSON;
|
import org.shredzone.acme4j.toolbox.JSON;
|
||||||
import org.shredzone.acme4j.toolbox.JSONBuilder;
|
import org.shredzone.acme4j.toolbox.JSONBuilder;
|
||||||
|
@ -286,8 +284,8 @@ public class CertificateTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testRenewalInfo() throws AcmeException, IOException {
|
public void testRenewalInfo() throws AcmeException, IOException {
|
||||||
var certId = "MFswCwYJYIZIAWUDBAIBBCCeWLRusNLb--vmWOkxm34qDjTMWkc3utIhOMoMwKDqbgQg2iiKWySZrD-6c88HMZ6vhIHZPamChLlzGHeZ7pTS8jYCCD6jRWhlRB8c";
|
// certid-cert.pem and certId provided by draft-ietf-acme-ari-03 and known good
|
||||||
// certid-cert.pem and certId provided by draft-ietf-acme-ari-01 and known good
|
var certId = "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE";
|
||||||
var certIdCert = TestUtils.createCertificate("/certid-cert.pem");
|
var certIdCert = TestUtils.createCertificate("/certid-cert.pem");
|
||||||
var certResourceUrl = new URL(resourceUrl.toExternalForm() + "/" + certId);
|
var certResourceUrl = new URL(resourceUrl.toExternalForm() + "/" + certId);
|
||||||
var retryAfterInstant = Instant.now().plus(10L, ChronoUnit.DAYS);
|
var retryAfterInstant = Instant.now().plus(10L, ChronoUnit.DAYS);
|
||||||
|
@ -339,7 +337,7 @@ public class CertificateTest {
|
||||||
provider.putTestResource(Resource.RENEWAL_INFO, resourceUrl);
|
provider.putTestResource(Resource.RENEWAL_INFO, resourceUrl);
|
||||||
|
|
||||||
var cert = new Certificate(provider.createLogin(), locationUrl);
|
var cert = new Certificate(provider.createLogin(), locationUrl);
|
||||||
assertThat(cert.getCertID()).isEqualTo(certId);
|
assertThat(cert.getCertID()).isEqualTo("MFgwCwYJYIZIAWUDBAIBBCCeWLRusNLb--vmWOkxm34qDjTMWkc3utIhOMoMwKDqbgQg2iiKWySZrD-6c88HMZ6vhIHZPamChLlzGHeZ7pTS8jYCBQCHZUMh");
|
||||||
assertThat(cert.hasRenewalInfo()).isTrue();
|
assertThat(cert.hasRenewalInfo()).isTrue();
|
||||||
assertThat(cert.getRenewalInfoLocation())
|
assertThat(cert.getRenewalInfoLocation())
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
|
@ -365,8 +363,8 @@ public class CertificateTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testMarkedAsReplaced() throws AcmeException, IOException {
|
public void testMarkedAsReplaced() throws AcmeException, IOException {
|
||||||
// certid-cert.pem and certId provided by draft-ietf-acme-ari-01 and known good
|
// certid-cert.pem and certId provided by draft-ietf-acme-ari-03 and known good
|
||||||
var certId = "MFswCwYJYIZIAWUDBAIBBCCeWLRusNLb--vmWOkxm34qDjTMWkc3utIhOMoMwKDqbgQg2iiKWySZrD-6c88HMZ6vhIHZPamChLlzGHeZ7pTS8jYCCD6jRWhlRB8c";
|
var certId = "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE";
|
||||||
var certIdCert = TestUtils.createCertificate("/certid-cert.pem");
|
var certIdCert = TestUtils.createCertificate("/certid-cert.pem");
|
||||||
var certResourceUrl = new URL(resourceUrl.toExternalForm() + "/" + certId);
|
var certResourceUrl = new URL(resourceUrl.toExternalForm() + "/" + certId);
|
||||||
|
|
||||||
|
@ -405,55 +403,11 @@ public class CertificateTest {
|
||||||
provider.putTestResource(Resource.RENEWAL_INFO, resourceUrl);
|
provider.putTestResource(Resource.RENEWAL_INFO, resourceUrl);
|
||||||
|
|
||||||
var cert = new Certificate(provider.createLogin(), locationUrl);
|
var cert = new Certificate(provider.createLogin(), locationUrl);
|
||||||
assertThat(cert.getCertID()).isEqualTo(certId);
|
|
||||||
assertThat(cert.hasRenewalInfo()).isTrue();
|
assertThat(cert.hasRenewalInfo()).isTrue();
|
||||||
assertThat(cert.getRenewalInfoLocation())
|
assertThat(cert.getRenewalInfoLocation())
|
||||||
.isNotEmpty()
|
.isNotEmpty()
|
||||||
.contains(certResourceUrl);
|
.contains(certResourceUrl);
|
||||||
|
|
||||||
cert.markAsReplaced();
|
|
||||||
|
|
||||||
provider.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that markAsReplaced() throws an exception if not supported.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testMarkedAsReplacedThrowsIfNotSupported() throws AcmeException, IOException {
|
|
||||||
var certIdCert = TestUtils.createCertificate("/certid-cert.pem");
|
|
||||||
|
|
||||||
var provider = new TestableConnectionProvider() {
|
|
||||||
private boolean certRequested = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int sendCertificateRequest(URL url, Login login) {
|
|
||||||
assertThat(url).isEqualTo(locationUrl);
|
|
||||||
assertThat(login).isNotNull();
|
|
||||||
certRequested = true;
|
|
||||||
return HttpURLConnection.HTTP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<X509Certificate> readCertificates() {
|
|
||||||
assertThat(certRequested).isTrue();
|
|
||||||
return certIdCert;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<URL> getLinks(String relation) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// We just need a dummy resource to create a directory
|
|
||||||
provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
|
|
||||||
|
|
||||||
assertThatExceptionOfType(AcmeNotSupportedException.class).isThrownBy(() -> {
|
|
||||||
var cert = new Certificate(provider.createLogin(), locationUrl);
|
|
||||||
cert.markAsReplaced();
|
|
||||||
});
|
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,7 @@ public class OrderBuilderTest {
|
||||||
.autoRenewalLifetime(validity)
|
.autoRenewalLifetime(validity)
|
||||||
.autoRenewalLifetimeAdjust(predate)
|
.autoRenewalLifetimeAdjust(predate)
|
||||||
.autoRenewalEnableGet()
|
.autoRenewalEnableGet()
|
||||||
|
.replaces("aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE")
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
try (var softly = new AutoCloseableSoftAssertions()) {
|
try (var softly = new AutoCloseableSoftAssertions()) {
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDMDCCAhigAwIBAgIIPqNFaGVEHxwwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
MIIBQzCB66ADAgECAgUAh2VDITAKBggqhkjOPQQDAjAVMRMwEQYDVQQDEwpFeGFt
|
||||||
AxMVbWluaWNhIHJvb3QgY2EgM2ExMzU2MB4XDTIyMDMxNzE3NTEwOVoXDTI0MDQx
|
cGxlIENBMCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMBYxFDAS
|
||||||
NjE3NTEwOVowFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEB
|
BgNVBAMTC2V4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeBZu
|
||||||
AQUAA4IBDwAwggEKAoIBAQCgm9K/c+il2Pf0f8qhgxn9SKqXq88cOm9ov9AVRbPA
|
7cbpAYNXZLbbh8rNIzuOoqOOtmxA1v7cRm//AwyMwWxyHz4zfwmBhcSrf47NUAFf
|
||||||
OWAAewqX2yUAwI4LZBGEgzGzTATkiXfoJ3cN3k39cH6tBbb3iSPuEn7OZpIk9D+e
|
qzLQ2PPQxdTXREYEnKMjMCEwHwYDVR0jBBgwFoAUaYhba4dGQEHhs3uEe6CuLN4B
|
||||||
3Q9/hX+N/jlWkaTB/FNA+7aE5IVWhmdczYilXa10V9r+RcvACJt0gsipBZVJ4jfJ
|
yNQwCgYIKoZIzj0EAwIDRwAwRAIge09+S5TZAlw5tgtiVvuERV6cT4mfutXIlwTb
|
||||||
HnWJJGRZzzxqG/xkQmpXxZO7nOPFc8SxYKWdfcgp+rjR2ogYhSz7BfKoVakGPbpX
|
+FYN/8oCIClDsqBklhB9KAelFiYt9+6FDj3z4KGVelYM5MdsO3pK
|
||||||
vZOuT9z4kkHra/WjwlkQhtHoTXdAxH3qC2UjMzO57Tx+otj0CxAv9O7CTJXISywB
|
|
||||||
vEVcmTSZkHS3eZtvvIwPx7I30ITRkYk/tLl1MbyB3SiZAgMBAAGjeDB2MA4GA1Ud
|
|
||||||
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0T
|
|
||||||
AQH/BAIwADAfBgNVHSMEGDAWgBQ4zzDRUaXHVKqlSTWkULGU4zGZpTAWBgNVHREE
|
|
||||||
DzANggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAx0aYvmCk7JYGNEXe
|
|
||||||
+hrOfKawkHYzWvA92cI/Oi6h+oSdHZ2UKzwFNf37cVKZ37FCrrv5pFP/xhhHvrNV
|
|
||||||
EnOx4IaF7OrnaTu5miZiUWuvRQP7ZGmGNFYbLTEF6/dj+WqyYdVaWzxRqHFu1ptC
|
|
||||||
TXysJCeyiGnR+KOOjOOQ9ZlO5JUK3OE4hagPLfaIpDDy6RXQt3ss0iNLuB1+IOtp
|
|
||||||
1URpvffLZQ8xPsEgOZyPWOcabTwJrtqBwily+lwPFn2mChUx846LwQfxtsXU/lJg
|
|
||||||
HX2RteNJx7YYNeX3Uf960mgo5an6vE8QNAsIoNHYrGyEmXDhTRe9mCHyiW2S7fZq
|
|
||||||
o9q12g==
|
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIDSzCCAjOgAwIBAgIIOhNWtJ7Igr0wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
MIIDSzCCAjOgAwIBAgIIOhNWtJ7Igr0wDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE
|
||||||
|
|
|
@ -11,5 +11,6 @@
|
||||||
"lifetime": 604800,
|
"lifetime": 604800,
|
||||||
"lifetime-adjust": 518400,
|
"lifetime-adjust": 518400,
|
||||||
"allow-certificate-get": true
|
"allow-certificate-get": true
|
||||||
}
|
},
|
||||||
|
"replaces": "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE"
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ Latest version:  IP identifier validation
|
* Supports [RFC 8738](https://tools.ietf.org/html/rfc8738) IP identifier validation
|
||||||
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
|
* Supports [RFC 8739](https://tools.ietf.org/html/rfc8739) short-term automatic certificate renewal (experimental)
|
||||||
* Supports [RFC 8823](https://tools.ietf.org/html/rfc8823) for S/MIME certificates (experimental)
|
* Supports [RFC 8823](https://tools.ietf.org/html/rfc8823) for S/MIME certificates (experimental)
|
||||||
* Supports [draft-ietf-acme-ari-01](https://www.ietf.org/archive/id/draft-ietf-acme-ari-01.html) for renewal information
|
* Supports [draft-ietf-acme-ari-03](https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html) for renewal information
|
||||||
* Easy to use Java API
|
* Easy to use Java API
|
||||||
* Requires JRE 11 or higher
|
* Requires JRE 11 or higher
|
||||||
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
|
* Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22)
|
||||||
|
|
|
@ -5,6 +5,9 @@ This document will help you migrate your code to the latest _acme4j_ version.
|
||||||
## Migration to Version 3.2.0
|
## Migration to Version 3.2.0
|
||||||
|
|
||||||
- Starting with this version, the `CSRBuilder` won't add the first domain as common name automatically. This permits the issuance of very long domain names, and should have no negative impact otherwise, as this field is usually ignored by CAs anyway. If you should encounter a problem here, you can use `CSRBuilder.setCommonName()` to set the first domain as common name manually. Discussion see [here](https://community.letsencrypt.org/t/questions-re-simplifying-issuance-for-very-long-domain-names/207925/11).
|
- Starting with this version, the `CSRBuilder` won't add the first domain as common name automatically. This permits the issuance of very long domain names, and should have no negative impact otherwise, as this field is usually ignored by CAs anyway. If you should encounter a problem here, you can use `CSRBuilder.setCommonName()` to set the first domain as common name manually. Discussion see [here](https://community.letsencrypt.org/t/questions-re-simplifying-issuance-for-very-long-domain-names/207925/11).
|
||||||
|
- acme4j was updated to support the latest [draft-ietf-acme-ari-03](https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html) now. It is a breaking change! If you use ARI, make sure your server supports the latest draft before updating to this version of acme4j.
|
||||||
|
- `Certificate.markAsReplace()` has been removed, because this method is not supported by [draft-ietf-acme-ari-03](https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html) anymore. To mark an existing certificate as replaced, use the new method `OrderBuilder.replaces()` now.
|
||||||
|
- `Certificate.getCertID()` is not needed in the ACME context anymore. This method has been marked as deprecated. In a future version of acme4j, it will be removed without replacement. Refer to the source code to see how the certificate ID is computed.
|
||||||
|
|
||||||
## Migration to Version 3.0.0
|
## Migration to Version 3.0.0
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,14 @@ There is no special path for renewing a certificate. To renew it, just [order](o
|
||||||
|
|
||||||
## Renewal Information
|
## Renewal Information
|
||||||
|
|
||||||
_acme4j_ supports the [draft-ietf-acme-ari-01](https://www.ietf.org/archive/id/draft-ietf-acme-ari-01.html) draft.
|
_acme4j_ supports the [draft-ietf-acme-ari-03](https://www.ietf.org/archive/id/draft-ietf-acme-ari-03.html) draft.
|
||||||
|
|
||||||
You can check if the CA offers renewal information by invoking `Certificate.hasRenewalInfo()`. If it does, you can get a suggested time window for certificate nenewal by invoking `Certificate.getRenewalInfo()`.
|
You can check if the CA offers renewal information by invoking `Certificate.hasRenewalInfo()`. If it does, you can get a suggested time window for certificate nenewal by invoking `Certificate.getRenewalInfo()`.
|
||||||
|
|
||||||
After you renewed the certificate and made sure the old certificate is not used anymore, you can invoke `Certificate.markAsReplaced()` on the _old_ certificate. It will show the CA that the renewal is completed, and that the old certificate can be revoked.
|
When renewing a certificate, you can use `OrderBuilder.replaces()` to mark your current certificate as the one being replaced. This step is optional though.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
The next version of the draft contains changes that are not backward compatible. _acme4j_ only supports version 01 of the draft at the moment. Also, because of the dynamic nature of the draft, all parts of the API that are related to this draft may be changed or removed without notice. SemVer rules do not apply here.
|
Starting with _acme4j_ v3.2.0, the now obsolete [draft-ietf-acme-ari-01](https://www.ietf.org/archive/id/draft-ietf-acme-ari-01.html) is not supported anymore! If your server requires the old draft, use _acme4j_ v3.1.1 until the CA upgraded its systems. Because of the dynamic nature of the draft, all parts of the API that are related to this draft may be changed or removed without notice. SemVer rules do not apply here.
|
||||||
|
|
||||||
## Short-Term Automatic Renewal
|
## Short-Term Automatic Renewal
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue