diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Account.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Account.java
index 7e89c8f3..86ebb085 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/Account.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Account.java
@@ -154,13 +154,36 @@ public class Account extends AcmeJsonResource {
if (domain.isEmpty()) {
throw new IllegalArgumentException("domain must not be empty");
}
+ return preAuthorize(Identifier.dns(domain));
+ }
+
+ /**
+ * Pre-authorizes an {@link Identifier}. The CA will check if it accepts the
+ * identifier for certification, and returns the necessary challenges.
+ *
+ * Some servers may not allow pre-authorization.
+ *
+ * It is not possible to pre-authorize wildcard domains.
+ *
+ * @param identifier
+ * {@link Identifier} to be pre-authorized.
+ * @return {@link Authorization} object for this identifier
+ * @throws AcmeException
+ * if the server does not allow pre-authorization
+ * @throws AcmeServerException
+ * if the server allows pre-authorization, but will refuse to issue a
+ * certificate for this identifier
+ * @since 2.3
+ */
+ public Authorization preAuthorize(Identifier identifier) throws AcmeException {
+ Objects.requireNonNull(identifier, "identifier");
URL newAuthzUrl = getSession().resourceUrl(Resource.NEW_AUTHZ);
- LOG.debug("preAuthorizeDomain {}", domain);
+ LOG.debug("preAuthorize {}", identifier);
try (Connection conn = connect()) {
JSONBuilder claims = new JSONBuilder();
- claims.put("identifier", Identifier.dns(domain).toMap());
+ claims.put("identifier", identifier.toMap());
conn.sendSignedRequest(newAuthzUrl, claims, getLogin());
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 de0e4005..e82d348c 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Order.java
@@ -70,13 +70,28 @@ public class Order extends AcmeJsonResource {
/**
* Gets the list of domain names to be ordered.
+ *
+ * @deprecated Use {@link #getIdentifiers()}. This method only returns DNS type
+ * identifiers for compatibility reasons.
*/
+ @Deprecated
public List getDomains() {
+ return getIdentifiers().stream()
+ .filter(i -> Identifier.DNS.equals(i.getType()))
+ .map(Identifier::getDomain)
+ .collect(toList());
+ }
+
+ /**
+ * Gets the list of {@link Identifier} to be ordered.
+ *
+ * @since 2.3
+ */
+ public List getIdentifiers() {
return Collections.unmodifiableList(getJSON().get("identifiers")
.asArray()
.stream()
- .map(Value::asObject)
- .map(it -> it.get("value").asString())
+ .map(Value::asIdentifier)
.collect(toList()));
}
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 d7b46eca..8bd40f61 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/OrderBuilder.java
@@ -65,8 +65,7 @@ public class OrderBuilder {
* @return itself
*/
public OrderBuilder domain(String domain) {
- identifierSet.add(Identifier.dns(domain));
- return this;
+ return identifier(Identifier.dns(domain));
}
/**
@@ -99,6 +98,32 @@ public class OrderBuilder {
return this;
}
+ /**
+ * Adds an {@link Identifier} to the order.
+ *
+ * @param identifier
+ * {@link Identifier} to be added to the order.
+ * @return itself
+ * @since 2.3
+ */
+ public OrderBuilder identifier(Identifier identifier) {
+ identifierSet.add(requireNonNull(identifier, "identifier"));
+ return this;
+ }
+
+ /**
+ * Adds a collection of {@link Identifier} to the order.
+ *
+ * @param identifiers
+ * Collection of {@link Identifier} to be added to the order.
+ * @return itself
+ * @since 2.3
+ */
+ public OrderBuilder identifiers(Collection identifiers) {
+ requireNonNull(identifiers, "identifiers").forEach(this::identifier);
+ return this;
+ }
+
/**
* Sets a "not before" date in the certificate. May be ignored by the CA.
*
diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java
index 753eb538..4a560ac5 100644
--- a/acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java
+++ b/acme4j-client/src/test/java/org/shredzone/acme4j/IdentifierTest.java
@@ -14,7 +14,7 @@
package org.shredzone.acme4j;
import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.*;
import java.util.Map;
@@ -94,4 +94,21 @@ public class IdentifierTest {
assertThat(idRef.equals(null), is(false));
}
+ @Test
+ public void testNull() {
+ try {
+ new Identifier(null, "123.456");
+ fail("accepted null");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+
+ try {
+ new Identifier("foo", null);
+ fail("accepted null");
+ } catch (NullPointerException ex) {
+ // expected
+ }
+ }
+
}
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 40938beb..d3758eb7 100644
--- a/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java
+++ b/acme4j-client/src/test/java/org/shredzone/acme4j/OrderBuilderTest.java
@@ -43,6 +43,7 @@ public class OrderBuilderTest {
* Test that a new {@link Order} can be created.
*/
@Test
+ @SuppressWarnings("deprecation")
public void testOrderCertificate() throws Exception {
Instant notBefore = parseTimestamp("2016-01-01T00:00:00Z");
Instant notAfter = parseTimestamp("2016-01-08T00:00:00Z");
@@ -76,6 +77,10 @@ public class OrderBuilderTest {
.domains("example.com", "www.example.com")
.domain("example.org")
.domains(Arrays.asList("m.example.com", "m.example.org"))
+ .identifier(Identifier.dns("d.example.com"))
+ .identifiers(Arrays.asList(
+ Identifier.dns("d2.example.com"),
+ new Identifier("ip", "192.168.1.2")))
.notBefore(notBefore)
.notAfter(notAfter)
.create();
@@ -83,7 +88,18 @@ public class OrderBuilderTest {
assertThat(order.getDomains(), containsInAnyOrder(
"example.com", "www.example.com",
"example.org",
- "m.example.com", "m.example.org"));
+ "m.example.com", "m.example.org",
+ "d.example.com",
+ "d2.example.com"));
+ assertThat(order.getIdentifiers(), containsInAnyOrder(
+ Identifier.dns("example.com"),
+ Identifier.dns("www.example.com"),
+ Identifier.dns("example.org"),
+ Identifier.dns("m.example.com"),
+ Identifier.dns("m.example.org"),
+ Identifier.dns("d.example.com"),
+ Identifier.dns("d2.example.com"),
+ new Identifier("ip", "192.168.1.2")));
assertThat(order.getNotBefore(), is(parseTimestamp("2016-01-01T00:10:00Z")));
assertThat(order.getNotAfter(), is(parseTimestamp("2016-01-08T00:10:00Z")));
assertThat(order.getExpires(), is(parseTimestamp("2016-01-10T00:00:00Z")));
diff --git a/acme4j-client/src/test/resources/json/requestOrderRequest.json b/acme4j-client/src/test/resources/json/requestOrderRequest.json
index c29a2195..ab9a2993 100644
--- a/acme4j-client/src/test/resources/json/requestOrderRequest.json
+++ b/acme4j-client/src/test/resources/json/requestOrderRequest.json
@@ -19,6 +19,18 @@
{
"type": "dns",
"value": "m.example.org"
+ },
+ {
+ "type": "dns",
+ "value": "d.example.com"
+ },
+ {
+ "type": "dns",
+ "value": "d2.example.com"
+ },
+ {
+ "type": "ip",
+ "value": "192.168.1.2"
}
],
"notBefore": "2016-01-01T00:00:00Z",
diff --git a/acme4j-client/src/test/resources/json/requestOrderResponse.json b/acme4j-client/src/test/resources/json/requestOrderResponse.json
index 09f642a2..07e0f2a8 100644
--- a/acme4j-client/src/test/resources/json/requestOrderResponse.json
+++ b/acme4j-client/src/test/resources/json/requestOrderResponse.json
@@ -21,6 +21,18 @@
{
"type": "dns",
"value": "m.example.org"
+ },
+ {
+ "type": "dns",
+ "value": "d.example.com"
+ },
+ {
+ "type": "dns",
+ "value": "d2.example.com"
+ },
+ {
+ "type": "ip",
+ "value": "192.168.1.2"
}
],
"notBefore": "2016-01-01T00:10:00Z",