diff --git a/acme4j-it/pom.xml b/acme4j-it/pom.xml
index 4a95b260..99979f8c 100644
--- a/acme4j-it/pom.xml
+++ b/acme4j-it/pom.xml
@@ -36,13 +36,22 @@
-
+
ci
false
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org/shredzone/acme4j/it/boulder/**
+
+
+
io.fabric8
docker-maven-plugin
@@ -67,6 +76,27 @@
+
+
+
+ boulder
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ org/shredzone/acme4j/it/pebble/**
+
+
+
+
+
+
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderHttpIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderHttpIT.java
new file mode 100644
index 00000000..d6278c9d
--- /dev/null
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderHttpIT.java
@@ -0,0 +1,153 @@
+/*
+ * acme4j - Java ACME client
+ *
+ * Copyright (C) 2017 Richard "Shred" Körber
+ * http://acme4j.shredzone.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+package org.shredzone.acme4j.it.boulder;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URI;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+import org.junit.Test;
+import org.shredzone.acme4j.Account;
+import org.shredzone.acme4j.AccountBuilder;
+import org.shredzone.acme4j.Authorization;
+import org.shredzone.acme4j.Certificate;
+import org.shredzone.acme4j.Order;
+import org.shredzone.acme4j.Session;
+import org.shredzone.acme4j.Status;
+import org.shredzone.acme4j.challenge.Http01Challenge;
+import org.shredzone.acme4j.exception.AcmeException;
+import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
+import org.shredzone.acme4j.it.BammBammClient;
+import org.shredzone.acme4j.util.CSRBuilder;
+import org.shredzone.acme4j.util.KeyPairUtils;
+
+/**
+ * Tests a complete certificate order with different challenges.
+ */
+public class OrderHttpIT {
+
+ private static final String TEST_DOMAIN = "example.com";
+
+ private final String bammbammUrl = System.getProperty("bammbammUrl", "http://localhost:14001");
+
+ private BammBammClient client = new BammBammClient(bammbammUrl);
+
+ /**
+ * Test if a certificate can be ordered via http-01 challenge.
+ */
+ @Test
+ public void testHttpValidation() throws Exception {
+ KeyPair keyPair = createKeyPair();
+ Session session = new Session(boulderURI(), keyPair);
+
+ Account account = new AccountBuilder()
+ .agreeToTermsOfService()
+ .create(session);
+
+ KeyPair domainKeyPair = createKeyPair();
+
+ Order order = account.newOrder().domain(TEST_DOMAIN).create();
+
+ for (Authorization auth : order.getAuthorizations()) {
+ Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
+ assertThat(challenge, is(notNullValue()));
+
+ client.httpAddToken(challenge.getToken(), challenge.getAuthorization());
+
+ challenge.trigger();
+
+ await()
+ .pollInterval(1, SECONDS)
+ .timeout(30, SECONDS)
+ .conditionEvaluationListener(cond -> updateAuth(auth))
+ .until(auth::getStatus, not(isOneOf(Status.PENDING, Status.PROCESSING)));
+
+ if (auth.getStatus() != Status.VALID) {
+ fail("Authorization failed");
+ }
+
+ client.httpRemoveToken(challenge.getToken());
+ }
+
+ CSRBuilder csr = new CSRBuilder();
+ csr.addDomain(TEST_DOMAIN);
+ csr.sign(domainKeyPair);
+ byte[] encodedCsr = csr.getEncoded();
+
+ order.execute(encodedCsr);
+
+ await()
+ .pollInterval(1, SECONDS)
+ .timeout(30, SECONDS)
+ .conditionEvaluationListener(cond -> updateOrder(order))
+ .until(order::getStatus, not(isOneOf(Status.PENDING, Status.PROCESSING)));
+
+ Certificate certificate = order.getCertificate();
+ X509Certificate cert = certificate.getCertificate();
+ assertThat(cert, not(nullValue()));
+ assertThat(cert.getNotAfter(), not(nullValue()));
+ assertThat(cert.getNotBefore(), not(nullValue()));
+ assertThat(cert.getSubjectX500Principal().getName(), containsString("CN=" + TEST_DOMAIN));
+ }
+
+ /**
+ * @return The {@link URI} of the Boulder server to test against.
+ */
+ protected URI boulderURI() {
+ return URI.create("http://localhost:4001/directory");
+ }
+
+ /**
+ * Creates a fresh key pair.
+ *
+ * @return Created new {@link KeyPair}
+ */
+ protected KeyPair createKeyPair() {
+ return KeyPairUtils.createKeyPair(2048);
+ }
+
+ /**
+ * Safely updates the authorization, catching checked exceptions.
+ *
+ * @param auth
+ * {@link Authorization} to update
+ */
+ private void updateAuth(Authorization auth) {
+ try {
+ auth.update();
+ } catch (AcmeException ex) {
+ throw new AcmeLazyLoadingException(auth, ex);
+ }
+ }
+
+ /**
+ * Safely updates the order, catching checked exceptions.
+ *
+ * @param order
+ * {@link Order} to update
+ */
+ private void updateOrder(Order order) {
+ try {
+ order.update();
+ } catch (AcmeException ex) {
+ throw new AcmeLazyLoadingException(order, ex);
+ }
+ }
+
+}
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderTlsSniIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderTlsSniIT.java
new file mode 100644
index 00000000..5652088d
--- /dev/null
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderTlsSniIT.java
@@ -0,0 +1,157 @@
+/*
+ * acme4j - Java ACME client
+ *
+ * Copyright (C) 2017 Richard "Shred" Körber
+ * http://acme4j.shredzone.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+package org.shredzone.acme4j.it.boulder;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.net.URI;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+import org.junit.Test;
+import org.shredzone.acme4j.Account;
+import org.shredzone.acme4j.AccountBuilder;
+import org.shredzone.acme4j.Authorization;
+import org.shredzone.acme4j.Certificate;
+import org.shredzone.acme4j.Order;
+import org.shredzone.acme4j.Session;
+import org.shredzone.acme4j.Status;
+import org.shredzone.acme4j.challenge.TlsSni02Challenge;
+import org.shredzone.acme4j.exception.AcmeException;
+import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
+import org.shredzone.acme4j.it.BammBammClient;
+import org.shredzone.acme4j.util.CSRBuilder;
+import org.shredzone.acme4j.util.CertificateUtils;
+import org.shredzone.acme4j.util.KeyPairUtils;
+
+/**
+ * Tests a complete certificate order with different challenges.
+ */
+public class OrderTlsSniIT {
+
+ private static final String TEST_DOMAIN = "example.com";
+
+ private final String bammbammUrl = System.getProperty("bammbammUrl", "http://localhost:14001");
+
+ private BammBammClient client = new BammBammClient(bammbammUrl);
+
+ /**
+ * Test if a certificate can be ordered via http-01 challenge.
+ */
+ @Test
+ public void testHttpValidation() throws Exception {
+ KeyPair keyPair = createKeyPair();
+ Session session = new Session(boulderURI(), keyPair);
+
+ Account account = new AccountBuilder()
+ .agreeToTermsOfService()
+ .create(session);
+
+ KeyPair domainKeyPair = createKeyPair();
+
+ Order order = account.newOrder().domain(TEST_DOMAIN).create();
+
+ for (Authorization auth : order.getAuthorizations()) {
+ TlsSni02Challenge challenge = auth.findChallenge(TlsSni02Challenge.TYPE);
+ assertThat(challenge, is(notNullValue()));
+
+ KeyPair challengeKeyPair = createKeyPair();
+ X509Certificate challengeCert = CertificateUtils.createTlsSni02Certificate(challengeKeyPair, challenge.getSubject(), challenge.getSanB());
+
+ client.tlsSniAddCertificate(challenge.getSubject(), challengeKeyPair.getPrivate(), challengeCert);
+
+ challenge.trigger();
+
+ await()
+ .pollInterval(1, SECONDS)
+ .timeout(30, SECONDS)
+ .conditionEvaluationListener(cond -> updateAuth(auth))
+ .until(auth::getStatus, not(isOneOf(Status.PENDING, Status.PROCESSING)));
+
+ if (auth.getStatus() != Status.VALID) {
+ fail("Authorization failed");
+ }
+
+ client.tlsSniRemoveCertificate(challenge.getSubject());
+ }
+
+ CSRBuilder csr = new CSRBuilder();
+ csr.addDomain(TEST_DOMAIN);
+ csr.sign(domainKeyPair);
+ byte[] encodedCsr = csr.getEncoded();
+
+ order.execute(encodedCsr);
+
+ await()
+ .pollInterval(1, SECONDS)
+ .timeout(30, SECONDS)
+ .conditionEvaluationListener(cond -> updateOrder(order))
+ .until(order::getStatus, not(isOneOf(Status.PENDING, Status.PROCESSING)));
+
+ Certificate certificate = order.getCertificate();
+ X509Certificate cert = certificate.getCertificate();
+ assertThat(cert, not(nullValue()));
+ assertThat(cert.getNotAfter(), not(nullValue()));
+ assertThat(cert.getNotBefore(), not(nullValue()));
+ assertThat(cert.getSubjectX500Principal().getName(), containsString("CN=" + TEST_DOMAIN));
+ }
+
+ /**
+ * @return The {@link URI} of the Boulder server to test against.
+ */
+ protected URI boulderURI() {
+ return URI.create("http://localhost:4001/directory");
+ }
+
+ /**
+ * Creates a fresh key pair.
+ *
+ * @return Created new {@link KeyPair}
+ */
+ protected KeyPair createKeyPair() {
+ return KeyPairUtils.createKeyPair(2048);
+ }
+
+ /**
+ * Safely updates the authorization, catching checked exceptions.
+ *
+ * @param auth
+ * {@link Authorization} to update
+ */
+ private void updateAuth(Authorization auth) {
+ try {
+ auth.update();
+ } catch (AcmeException ex) {
+ throw new AcmeLazyLoadingException(auth, ex);
+ }
+ }
+
+ /**
+ * Safely updates the order, catching checked exceptions.
+ *
+ * @param order
+ * {@link Order} to update
+ */
+ private void updateOrder(Order order) {
+ try {
+ order.update();
+ } catch (AcmeException ex) {
+ throw new AcmeLazyLoadingException(order, ex);
+ }
+ }
+
+}
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/AccountIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/AccountIT.java
similarity index 99%
rename from acme4j-it/src/test/java/org/shredzone/acme4j/it/AccountIT.java
rename to acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/AccountIT.java
index 54182c0b..dc6e32e8 100644
--- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/AccountIT.java
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/AccountIT.java
@@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
-package org.shredzone.acme4j.it;
+package org.shredzone.acme4j.it.pebble;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderIT.java
similarity index 98%
rename from acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderIT.java
rename to acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderIT.java
index 5103df8c..c2aca0d1 100644
--- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderIT.java
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderIT.java
@@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
-package org.shredzone.acme4j.it;
+package org.shredzone.acme4j.it.pebble;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
@@ -36,6 +36,7 @@ import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
+import org.shredzone.acme4j.it.BammBammClient;
import org.shredzone.acme4j.util.CSRBuilder;
import org.shredzone.acme4j.util.CertificateUtils;
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderWildcardIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderWildcardIT.java
similarity index 98%
rename from acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderWildcardIT.java
rename to acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderWildcardIT.java
index 10bce357..9142e3fe 100644
--- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/OrderWildcardIT.java
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/OrderWildcardIT.java
@@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
-package org.shredzone.acme4j.it;
+package org.shredzone.acme4j.it.pebble;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.stream.Collectors.toList;
@@ -36,6 +36,7 @@ import org.shredzone.acme4j.Order;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.challenge.Dns01Challenge;
+import org.shredzone.acme4j.it.BammBammClient;
import org.shredzone.acme4j.util.CSRBuilder;
/**
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/PebbleITBase.java
similarity index 98%
rename from acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java
rename to acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/PebbleITBase.java
index fd344a0a..ff233d7b 100644
--- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/PebbleITBase.java
@@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
-package org.shredzone.acme4j.it;
+package org.shredzone.acme4j.it.pebble;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
@@ -27,6 +27,7 @@ import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.Order;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeLazyLoadingException;
+import org.shredzone.acme4j.it.BammBammClient;
import org.shredzone.acme4j.util.KeyPairUtils;
/**
diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/SessionIT.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/SessionIT.java
similarity index 98%
rename from acme4j-it/src/test/java/org/shredzone/acme4j/it/SessionIT.java
rename to acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/SessionIT.java
index f3fda030..e31ac718 100644
--- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/SessionIT.java
+++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/pebble/SessionIT.java
@@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
-package org.shredzone.acme4j.it;
+package org.shredzone.acme4j.it.pebble;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
diff --git a/pom.xml b/pom.xml
index b3b040c2..5a9691fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.19.1
+ 2.20.1
classes
10
@@ -92,7 +92,7 @@
org.apache.maven.plugins
maven-failsafe-plugin
- 2.19.1
+ 2.20.1