From d53e87883585c2b0526375137e57c21084fd4367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 8 Jan 2018 00:51:24 +0100 Subject: [PATCH] Add integration tests for Boulder --- acme4j-it/pom.xml | 32 +++- .../acme4j/it/boulder/OrderHttpIT.java | 153 +++++++++++++++++ .../acme4j/it/boulder/OrderTlsSniIT.java | 157 ++++++++++++++++++ .../acme4j/it/{ => pebble}/AccountIT.java | 2 +- .../acme4j/it/{ => pebble}/OrderIT.java | 3 +- .../it/{ => pebble}/OrderWildcardIT.java | 3 +- .../acme4j/it/{ => pebble}/PebbleITBase.java | 3 +- .../acme4j/it/{ => pebble}/SessionIT.java | 2 +- pom.xml | 4 +- 9 files changed, 351 insertions(+), 8 deletions(-) create mode 100644 acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderHttpIT.java create mode 100644 acme4j-it/src/test/java/org/shredzone/acme4j/it/boulder/OrderTlsSniIT.java rename acme4j-it/src/test/java/org/shredzone/acme4j/it/{ => pebble}/AccountIT.java (99%) rename acme4j-it/src/test/java/org/shredzone/acme4j/it/{ => pebble}/OrderIT.java (98%) rename acme4j-it/src/test/java/org/shredzone/acme4j/it/{ => pebble}/OrderWildcardIT.java (98%) rename acme4j-it/src/test/java/org/shredzone/acme4j/it/{ => pebble}/PebbleITBase.java (98%) rename acme4j-it/src/test/java/org/shredzone/acme4j/it/{ => pebble}/SessionIT.java (98%) 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