mirror of https://github.com/shred/acme4j
Update to draft-ietf-acme-ari-05
parent
afa60ae76f
commit
0ccd68c09a
|
@ -15,7 +15,7 @@ This Java client helps connecting to an ACME server, and performing all necessar
|
||||||
* 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 [RFC 9444](https://tools.ietf.org/html/rfc9444) for subdomain validation
|
* Supports [RFC 9444](https://tools.ietf.org/html/rfc9444) for subdomain validation
|
||||||
* Supports [draft-ietf-acme-ari-04](https://www.ietf.org/archive/id/draft-ietf-acme-ari-04.html) for renewal information (experimental)
|
* Supports [draft-ietf-acme-ari-05](https://www.ietf.org/archive/id/draft-ietf-acme-ari-05.html) for renewal information (experimental)
|
||||||
* Easy to use Java API
|
* Easy to use Java API
|
||||||
* Requires JRE 11 or higher
|
* Requires JRE 11 or higher
|
||||||
* Supports [Let's Encrypt](https://letsencrypt.org/), [SSL.com](https://www.ssl.com/), [ZeroSSL](https://zerossl.com), and all other CAs that comply with the ACME protocol (RFC 8555). Note that _acme4j_ is an independent project that is not supported or endorsed by any of the CAs.
|
* Supports [Let's Encrypt](https://letsencrypt.org/), [SSL.com](https://www.ssl.com/), [ZeroSSL](https://zerossl.com), and all other CAs that comply with the ACME protocol (RFC 8555). Note that _acme4j_ is an independent project that is not supported or endorsed by any of the CAs.
|
||||||
|
|
|
@ -343,6 +343,10 @@ public class OrderBuilder {
|
||||||
throw new AcmeNotSupportedException("auto-renewal");
|
throw new AcmeNotSupportedException("auto-renewal");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replaces != null && session.resourceUrlOptional(Resource.RENEWAL_INFO).isEmpty()) {
|
||||||
|
throw new AcmeNotSupportedException("renewal-information");
|
||||||
|
}
|
||||||
|
|
||||||
var hasAncestorDomain = identifierSet.stream()
|
var hasAncestorDomain = identifierSet.stream()
|
||||||
.filter(id -> Identifier.TYPE_DNS.equals(id.getType()))
|
.filter(id -> Identifier.TYPE_DNS.equals(id.getType()))
|
||||||
.anyMatch(id -> id.toMap().containsKey(Identifier.KEY_ANCESTOR_DOMAIN));
|
.anyMatch(id -> id.toMap().containsKey(Identifier.KEY_ANCESTOR_DOMAIN));
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
package org.shredzone.acme4j;
|
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.*;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp;
|
import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp;
|
||||||
import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
|
import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
|
||||||
|
@ -169,7 +168,6 @@ 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()) {
|
||||||
|
@ -335,4 +333,73 @@ public class OrderBuilderTest {
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the ARI replaces field is set.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testARIReplaces() throws Exception {
|
||||||
|
var provider = new TestableConnectionProvider() {
|
||||||
|
@Override
|
||||||
|
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
|
||||||
|
assertThat(url).isEqualTo(resourceUrl);
|
||||||
|
assertThatJson(claims.toString()).isEqualTo(getJSON("requestReplacesRequest").toString());
|
||||||
|
assertThat(login).isNotNull();
|
||||||
|
return HttpURLConnection.HTTP_CREATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSON readJsonResponse() {
|
||||||
|
return getJSON("requestReplacesResponse");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URL getLocation() {
|
||||||
|
return locationUrl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var login = provider.createLogin();
|
||||||
|
|
||||||
|
provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
|
||||||
|
provider.putTestResource(Resource.RENEWAL_INFO, resourceUrl);
|
||||||
|
|
||||||
|
var account = new Account(login);
|
||||||
|
account.newOrder()
|
||||||
|
.domain("example.org")
|
||||||
|
.replaces("aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE")
|
||||||
|
.create();
|
||||||
|
|
||||||
|
provider.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that exception is thrown if the ARI replaces field is set but ARI is not
|
||||||
|
* supported.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testARIReplaceFails() throws Exception {
|
||||||
|
var provider = new TestableConnectionProvider() {
|
||||||
|
@Override
|
||||||
|
public int sendSignedRequest(URL url, JSONBuilder claims, Login login) {
|
||||||
|
fail("Request was sent");
|
||||||
|
return HttpURLConnection.HTTP_FORBIDDEN;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var login = provider.createLogin();
|
||||||
|
|
||||||
|
provider.putTestResource(Resource.NEW_ORDER, resourceUrl);
|
||||||
|
|
||||||
|
var account = new Account(login);
|
||||||
|
assertThatExceptionOfType(AcmeNotSupportedException.class).isThrownBy(() -> {
|
||||||
|
account.newOrder()
|
||||||
|
.domain("example.org")
|
||||||
|
.replaces("aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE")
|
||||||
|
.create();
|
||||||
|
})
|
||||||
|
.withMessage("Server does not support renewal-information");
|
||||||
|
|
||||||
|
provider.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,5 @@
|
||||||
"lifetime": 604800,
|
"lifetime": 604800,
|
||||||
"lifetime-adjust": 518400,
|
"lifetime-adjust": 518400,
|
||||||
"allow-certificate-get": true
|
"allow-certificate-get": true
|
||||||
},
|
}
|
||||||
"replaces": "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"identifiers": [
|
||||||
|
{
|
||||||
|
"type": "dns",
|
||||||
|
"value": "example.org"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"replaces": "aYhba4dGQEHhs3uEe6CuLN4ByNQ.AIdlQyE"
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"status": "pending",
|
||||||
|
"expires": "2016-01-10T00:00:00Z",
|
||||||
|
"identifiers": [
|
||||||
|
{
|
||||||
|
"type": "dns",
|
||||||
|
"value": "example.org"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authorizations": [
|
||||||
|
"https://example.com/acme/authz/1234",
|
||||||
|
"https://example.com/acme/authz/2345"
|
||||||
|
],
|
||||||
|
"finalize": "https://example.com/acme/acct/1/order/1/finalize"
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ Latest version:  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 [RFC 9444](https://tools.ietf.org/html/rfc9444) for subdomain validation
|
* Supports [RFC 9444](https://tools.ietf.org/html/rfc9444) for subdomain validation
|
||||||
* Supports [draft-ietf-acme-ari-04](https://www.ietf.org/archive/id/draft-ietf-acme-ari-04.html) for renewal information (experimental)
|
* Supports [draft-ietf-acme-ari-05](https://www.ietf.org/archive/id/draft-ietf-acme-ari-05.html) for renewal information (experimental)
|
||||||
* Easy to use Java API
|
* Easy to use Java API
|
||||||
* Requires JRE 11 or higher
|
* Requires JRE 11 or higher
|
||||||
* Supports [Let's Encrypt](https://letsencrypt.org/), [SSL.com](https://www.ssl.com/), [ZeroSSL](https://zerossl.com), and all other CAs that comply with the ACME protocol (RFC 8555). Note that _acme4j_ is an independent project that is not supported or endorsed by any of the CAs.
|
* Supports [Let's Encrypt](https://letsencrypt.org/), [SSL.com](https://www.ssl.com/), [ZeroSSL](https://zerossl.com), and all other CAs that comply with the ACME protocol (RFC 8555). Note that _acme4j_ is an independent project that is not supported or endorsed by any of the CAs.
|
||||||
|
|
|
@ -13,7 +13,7 @@ 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-04](https://www.ietf.org/archive/id/draft-ietf-acme-ari-04.html) draft.
|
_acme4j_ supports the [draft-ietf-acme-ari-05](https://www.ietf.org/archive/id/draft-ietf-acme-ari-05.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()`.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue