From 0afee7a9e82e3fb56a9b44e9409bbd3cc22902c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 16 Mar 2019 16:53:14 +0100 Subject: [PATCH] Add acme-star's recurrent-certificate-predate field --- .../main/java/org/shredzone/acme4j/Order.java | 13 ++++++++++++ .../org/shredzone/acme4j/OrderBuilder.java | 21 +++++++++++++++++++ .../shredzone/acme4j/OrderBuilderTest.java | 3 +++ .../java/org/shredzone/acme4j/OrderTest.java | 3 +++ .../json/finalizeRecurrentResponse.json | 1 + .../json/requestRecurrentOrderRequest.json | 1 + .../json/requestRecurrentOrderResponse.json | 1 + .../json/updateRecurrentOrderResponse.json | 1 + src/site/markdown/usage/order.md | 12 +++++------ 9 files changed, 50 insertions(+), 6 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java index 00adb7d7..66fbd20e 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java @@ -224,6 +224,19 @@ public class Order extends AcmeJsonResource { .orElse(null); } + /** + * Returns the predate period of each certificate, or {@code null}. + * + * @since 2.7 + */ + @CheckForNull + public Duration getRecurrentCertificatePredate() { + return getJSON().get("recurrent-certificate-predate") + .optional() + .map(Value::asDuration) + .orElse(null); + } + /** * Returns {@code true} if STAR certificates from this order can also be fetched via * GET requests. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java index 13796554..905d6688 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java @@ -50,6 +50,7 @@ public class OrderBuilder { private Instant recurrentStart; private Instant recurrentEnd; private Duration recurrentValidity; + private Duration recurrentPredate; private boolean recurrentGet; /** @@ -229,6 +230,23 @@ public class OrderBuilder { return this; } + /** + * Sets the amount of pre-dating each certificate. If not set, the CA's + * default (0) is used. + *

+ * Implies {@link #recurrent()}. + * + * @param duration + * Duration of certificate pre-dating + * @return itself + * @since 2.7 + */ + public OrderBuilder recurrentCertificatePredate(Duration duration) { + recurrent(); + this.recurrentPredate = requireNonNull(duration, "duration"); + return this; + } + /** * Announces that the client wishes to fetch the recurring certificate via GET * request. If not used, the STAR certificate can only be fetched via POST-as-GET @@ -288,6 +306,9 @@ public class OrderBuilder { if (recurrentValidity != null) { claims.put("recurrent-certificate-validity", recurrentValidity); } + if (recurrentPredate != null) { + claims.put("recurrent-certificate-predate", recurrentPredate); + } if (recurrentGet) { claims.put("recurrent-certificate-get", recurrentGet); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java index d3514fb0..892a6547 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java @@ -115,6 +115,7 @@ public class OrderBuilderTest { Instant recurrentStart = parseTimestamp("2018-01-01T00:00:00Z"); Instant recurrentEnd = parseTimestamp("2019-01-01T00:00:00Z"); Duration validity = Duration.ofDays(7); + Duration predate = Duration.ofDays(6); TestableConnectionProvider provider = new TestableConnectionProvider() { @Override @@ -148,6 +149,7 @@ public class OrderBuilderTest { .recurrentStart(recurrentStart) .recurrentEnd(recurrentEnd) .recurrentCertificateValidity(validity) + .recurrentCertificatePredate(predate) .recurrentEnableGet() .create(); @@ -158,6 +160,7 @@ public class OrderBuilderTest { assertThat(order.getRecurrentStart(), is(recurrentStart)); assertThat(order.getRecurrentEnd(), is(recurrentEnd)); assertThat(order.getRecurrentCertificateValidity(), is(validity)); + assertThat(order.getRecurrentCertificatePredate(), is(predate)); assertThat(order.isRecurrentGetEnabled(), is(true)); assertThat(order.getLocation(), is(locationUrl)); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java index fbb26a94..f240791e 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/OrderTest.java @@ -84,6 +84,7 @@ public class OrderTest { assertThat(order.getRecurrentStart(), is(nullValue())); assertThat(order.getRecurrentEnd(), is(nullValue())); assertThat(order.getRecurrentCertificateValidity(), is(nullValue())); + assertThat(order.getRecurrentCertificatePredate(), is(nullValue())); assertThat(order.isRecurrentGetEnabled(), is(false)); assertThat(order.getError(), is(notNullValue())); @@ -243,6 +244,7 @@ public class OrderTest { assertThat(order.getRecurrentStart(), is(parseTimestamp("2016-01-01T00:00:00Z"))); assertThat(order.getRecurrentEnd(), is(parseTimestamp("2017-01-01T00:00:00Z"))); assertThat(order.getRecurrentCertificateValidity(), is(Duration.ofHours(168))); + assertThat(order.getRecurrentCertificatePredate(), is(Duration.ofDays(6))); assertThat(order.getNotBefore(), is(nullValue())); assertThat(order.getNotAfter(), is(nullValue())); assertThat(order.isRecurrentGetEnabled(), is(true)); @@ -282,6 +284,7 @@ public class OrderTest { assertThat(order.getRecurrentStart(), is(parseTimestamp("2018-01-01T00:00:00Z"))); assertThat(order.getRecurrentEnd(), is(parseTimestamp("2019-01-01T00:00:00Z"))); assertThat(order.getRecurrentCertificateValidity(), is(Duration.ofHours(168))); + assertThat(order.getRecurrentCertificatePredate(), is(Duration.ofDays(6))); assertThat(order.getNotBefore(), is(nullValue())); assertThat(order.getNotAfter(), is(nullValue())); assertThat(order.isRecurrentGetEnabled(), is(true)); diff --git a/acme4j-client/src/test/resources/json/finalizeRecurrentResponse.json b/acme4j-client/src/test/resources/json/finalizeRecurrentResponse.json index f4698875..05caef56 100644 --- a/acme4j-client/src/test/resources/json/finalizeRecurrentResponse.json +++ b/acme4j-client/src/test/resources/json/finalizeRecurrentResponse.json @@ -15,6 +15,7 @@ "recurrent-start-date": "2018-01-01T00:00:00Z", "recurrent-end-date": "2019-01-01T00:00:00Z", "recurrent-certificate-validity": 604800, + "recurrent-certificate-predate": 518400, "recurrent-certificate-get": true, "authorizations": [ "https://example.com/acme/authz/1234", diff --git a/acme4j-client/src/test/resources/json/requestRecurrentOrderRequest.json b/acme4j-client/src/test/resources/json/requestRecurrentOrderRequest.json index 1aeb5c75..cadd4856 100644 --- a/acme4j-client/src/test/resources/json/requestRecurrentOrderRequest.json +++ b/acme4j-client/src/test/resources/json/requestRecurrentOrderRequest.json @@ -9,5 +9,6 @@ "recurrent-start-date": "2018-01-01T00:00:00Z", "recurrent-end-date": "2019-01-01T00:00:00Z", "recurrent-certificate-validity": 604800, + "recurrent-certificate-predate": 518400, "recurrent-certificate-get": true } diff --git a/acme4j-client/src/test/resources/json/requestRecurrentOrderResponse.json b/acme4j-client/src/test/resources/json/requestRecurrentOrderResponse.json index 60f5fc7e..24193fb4 100644 --- a/acme4j-client/src/test/resources/json/requestRecurrentOrderResponse.json +++ b/acme4j-client/src/test/resources/json/requestRecurrentOrderResponse.json @@ -11,6 +11,7 @@ "recurrent-start-date": "2018-01-01T00:00:00Z", "recurrent-end-date": "2019-01-01T00:00:00Z", "recurrent-certificate-validity": 604800, + "recurrent-certificate-predate": 518400, "recurrent-certificate-get": true, "authorizations": [ "https://example.com/acme/authz/1234", diff --git a/acme4j-client/src/test/resources/json/updateRecurrentOrderResponse.json b/acme4j-client/src/test/resources/json/updateRecurrentOrderResponse.json index 7386f337..3a0784bc 100644 --- a/acme4j-client/src/test/resources/json/updateRecurrentOrderResponse.json +++ b/acme4j-client/src/test/resources/json/updateRecurrentOrderResponse.json @@ -4,5 +4,6 @@ "recurrent-start-date": "2016-01-01T00:00:00Z", "recurrent-end-date": "2017-01-01T00:00:00Z", "recurrent-certificate-validity": 604800, + "recurrent-certificate-predate": 518400, "recurrent-certificate-get": true } diff --git a/src/site/markdown/usage/order.md b/src/site/markdown/usage/order.md index 2b009e00..87e34cf9 100644 --- a/src/site/markdown/usage/order.md +++ b/src/site/markdown/usage/order.md @@ -197,6 +197,11 @@ byte[] csr = csrb.getEncoded(); _acme4j_ supports the [ACME STAR](https://tools.ietf.org/html/draft-ietf-acme-star) extension for short-term automatic renewal of certificates. +

+ To find out if the CA supports the STAR extension, check the metadata: ```java @@ -214,7 +219,7 @@ Order order = account.newOrder() .create(); ``` -You can use `recurrentStart()`, `recurrentEnd()` and `recurrentCertificateValidity()` to change the time span and frequency of automatic renewals. You cannot use `notBefore()` and `notAfter()` in combination with `recurrent()` though. +You can use `recurrentStart()`, `recurrentEnd()`, `recurrentCertificateValidity()` and `recurrentCertificatePredate()` to change the time span and frequency of automatic renewals. You cannot use `notBefore()` and `notAfter()` in combination with `recurrent()` though. The `Metadata` object also holds the accepted renewal limits (see `Metadata.getStarMinCertValidity()` and `Metadata.getStarMaxRenewal()`). @@ -239,8 +244,3 @@ X509Certificate latestCertificate = cert.getCertificate(); If supported by the CA, it is possible to negotiate that the certificate can also be downloaded via `GET` request. First use `Metadata.isStarCertificateGetAllowed()` to check if this option is supported by the CA. If it is, add `recurrentEnableGet()` to the order parameters to enable it. After the order was finalized, you can use any HTTP client to download the latest certificate from the certificate URL by a `GET` request. Use `Order.cancelRecurrent()` to terminate automatical certificate renewals. - -