diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AccountTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AccountTest.java new file mode 100644 index 00000000..d01440da --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AccountTest.java @@ -0,0 +1,52 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +import java.io.IOException; +import java.security.KeyPair; + +import org.junit.Test; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link Account}. + * + * @author Richard "Shred" Körber + */ +public class AccountTest { + + /** + * Test getters and setters. + */ + @Test + public void testGetterAndSetter() throws IOException { + KeyPair keypair = TestUtils.createKeyPair(); + + Account account = new Account(keypair); + + assertThat(account.getKeyPair(), is(sameInstance(keypair))); + } + + /** + * Test null values. + */ + @Test(expected = NullPointerException.class) + public void testNull() { + new Account(null); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index 9dbdd417..77146881 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -64,6 +64,32 @@ public class AuthorizationTest { authorization.setCombinations(Collections.unmodifiableList(combinations)); } + /** + * Test getters and setters. + */ + @Test + public void testGetterAndSetter() { + Authorization auth = new Authorization(); + + assertThat(auth.getDomain(), is(nullValue())); + assertThat(auth.getStatus(), is(nullValue())); + assertThat(auth.getExpires(), is(nullValue())); + assertThat(auth.getChallenges(), is(nullValue())); + assertThat(auth.getCombinations(), is(nullValue())); + + auth.setDomain("example.com"); + auth.setStatus("invalid"); + auth.setExpires("2015-12-12T17:19:36.336785823Z"); + auth.setChallenges(authorization.getChallenges()); + auth.setCombinations(authorization.getCombinations()); + + assertThat(auth.getDomain(), is("example.com")); + assertThat(auth.getStatus(), is("invalid")); + assertThat(auth.getExpires(), is("2015-12-12T17:19:36.336785823Z")); + assertThat(auth.getChallenges(), is(sameInstance(authorization.getChallenges()))); + assertThat(auth.getCombinations(), is(sameInstance(authorization.getCombinations()))); + } + /** * Test that {@link Authorization#findChallenge(String)} does only find standalone * challenges, and nothing else. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java new file mode 100644 index 00000000..2d81104d --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java @@ -0,0 +1,52 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.Test; + +/** + * Unit tests for {@link Registration}. + * + * @author Richard "Shred" Körber + */ +public class RegistrationTest { + + /** + * Test getters and setters. + */ + @Test + public void testGetterAndSetter() throws URISyntaxException { + Registration registration = new Registration(); + + assertThat(registration.getAgreementUrl(), is(nullValue())); + assertThat(registration.getLocation(), is(nullValue())); + assertThat(registration.getContacts(), is(empty())); + + registration.setAgreementUrl("http://example.com/agreement.pdf"); + registration.setLocation(new URI("http://example.com/acme/12345")); + registration.getContacts().add("mailto:foo@example.com"); + registration.getContacts().add("mailto:bar@example.com"); + + assertThat(registration.getAgreementUrl(), is("http://example.com/agreement.pdf")); + assertThat(registration.getLocation(), is(new URI("http://example.com/acme/12345"))); + assertThat(registration.getContacts(), contains("mailto:foo@example.com", "mailto:bar@example.com")); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java new file mode 100644 index 00000000..da04f0f8 --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java @@ -0,0 +1,74 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.challenge; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.io.IOException; +import java.security.KeyPair; + +import org.junit.Test; +import org.shredzone.acme4j.Account; +import org.shredzone.acme4j.challenge.Challenge.Status; +import org.shredzone.acme4j.util.ClaimBuilder; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link DnsChallenge}. + * + * @author Richard "Shred" Körber + */ +public class DnsChallengeTest { + + private static final String TOKEN = + "pNvmJivs0WCko2suV7fhe-59oFqyYx_yB7tx6kIMAyE"; + private static final String KEY_AUTHORIZATION = + "pNvmJivs0WCko2suV7fhe-59oFqyYx_yB7tx6kIMAyE.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0"; + + /** + * Test that {@link DnsChallenge} generates a correct authorization key. + */ + @Test + public void testHttpChallenge() throws IOException { + KeyPair keypair = TestUtils.createKeyPair(); + Account account = new Account(keypair); + + DnsChallenge challenge = new DnsChallenge(); + challenge.unmarshall(TestUtils.getResourceAsJsonMap("/dnsChallenge.json")); + + assertThat(challenge.getType(), is(DnsChallenge.TYPE)); + assertThat(challenge.getStatus(), is(Status.PENDING)); + + try { + challenge.getAuthorization(); + fail("getAuthorization() without previous authorize()"); + } catch (IllegalStateException ex) { + // expected + } + + challenge.authorize(account); + + assertThat(challenge.getToken(), is(TOKEN)); + assertThat(challenge.getAuthorization(), is(KEY_AUTHORIZATION)); + + ClaimBuilder cb = new ClaimBuilder(); + challenge.marshall(cb); + + assertThat(cb.toString(), sameJSONAs("{\"keyAuthorization\"=\"" + + KEY_AUTHORIZATION + "\"}").allowingExtraUnexpectedFields()); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java new file mode 100644 index 00000000..d18e3a84 --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java @@ -0,0 +1,126 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.challenge; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.KeyPair; + +import org.jose4j.base64url.Base64Url; +import org.jose4j.json.JsonUtil; +import org.jose4j.jwk.JsonWebKey; +import org.jose4j.jwk.JsonWebKey.OutputControlLevel; +import org.jose4j.lang.JoseException; +import org.junit.Test; +import org.shredzone.acme4j.challenge.Challenge.Status; +import org.shredzone.acme4j.util.ClaimBuilder; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link GenericChallenge}. + * + * @author Richard "Shred" Körber + */ +public class GenericChallengeTest { + + /** + * Test that after unmarshalling, the challenge properties are set correctly. + */ + @Test + public void testUnmarshall() throws IOException, URISyntaxException { + GenericChallenge challenge = new GenericChallenge(); + + // Test default values + assertThat(challenge.getType(), is(nullValue())); + assertThat(challenge.getStatus(), is(Status.PENDING)); + assertThat(challenge.getUri(), is(nullValue())); + assertThat(challenge.getValidated(), is(nullValue())); + + // Unmarshall a challenge JSON + challenge.unmarshall(TestUtils.getResourceAsJsonMap("/genericChallenge.json")); + + // Test unmarshalled values + assertThat(challenge.getType(), is("generic-01")); + assertThat(challenge.getStatus(), is(Status.VALID)); + assertThat(challenge.getUri(), is(new URI("http://example.com/challenge/123"))); + assertThat(challenge.getValidated(), is("2015-12-12T17:19:36.336785823Z")); + } + + /** + * Test get and put methods. + */ + public void testGetPut() { + GenericChallenge challenge = new GenericChallenge(); + + challenge.put("a-string", "foo"); + challenge.put("a-number", 1234); + + assertThat((String) challenge.get("a-string"), is("foo")); + assertThat((Integer) challenge.get("a-number"), is(1234)); + + ClaimBuilder cb = new ClaimBuilder(); + challenge.marshall(cb); + assertThat(cb.toString(), sameJSONAs("{\"a-string\":\"foo\",\"a-number\":1234}") + .allowingExtraUnexpectedFields()); + } + + /** + * Test that marshalling results in an identical JSON like the one that was + * unmarshalled. + */ + @Test + public void testMarshall() throws IOException, JoseException { + String json = TestUtils.getResourceAsString("/genericChallenge.json"); + + GenericChallenge challenge = new GenericChallenge(); + challenge.unmarshall(JsonUtil.parseJson(json)); + + ClaimBuilder cb = new ClaimBuilder(); + challenge.marshall(cb); + + assertThat(cb.toString(), sameJSONAs(json)); + } + + /** + * Test that the test keypair's thumbprint is correct. + */ + @Test + public void testJwkThumbprint() throws IOException, JoseException { + StringBuilder json = new StringBuilder(); + json.append('{'); + json.append("\"e\":\"").append(TestUtils.E).append("\","); + json.append("\"kty\":\"").append(TestUtils.KTY).append("\","); + json.append("\"n\":\"").append(TestUtils.N).append("\""); + json.append('}'); + + KeyPair keypair = TestUtils.createKeyPair(); + + // Test the JWK raw output. The JSON string must match the assert string + // exactly, as the thumbprint is a digest of that string. + final JsonWebKey jwk = JsonWebKey.Factory.newJwk(keypair.getPublic()); + ClaimBuilder cb = new ClaimBuilder(); + cb.putAll(jwk.toParams(OutputControlLevel.PUBLIC_ONLY)); + assertThat(cb.toString(), is(json.toString())); + + // Make sure the returned thumbprint is correct + byte[] thumbprint = GenericChallenge.jwkThumbprint(keypair.getPublic()); + assertThat(thumbprint, is(Base64Url.decode(TestUtils.THUMBPRINT))); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java new file mode 100644 index 00000000..9601db63 --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java @@ -0,0 +1,74 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.challenge; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.io.IOException; +import java.security.KeyPair; + +import org.junit.Test; +import org.shredzone.acme4j.Account; +import org.shredzone.acme4j.challenge.Challenge.Status; +import org.shredzone.acme4j.util.ClaimBuilder; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link HttpChallenge}. + * + * @author Richard "Shred" Körber + */ +public class HttpChallengeTest { + + private static final String TOKEN = + "rSoI9JpyvFi-ltdnBW0W1DjKstzG7cHixjzcOjwzAEQ"; + private static final String KEY_AUTHORIZATION = + "rSoI9JpyvFi-ltdnBW0W1DjKstzG7cHixjzcOjwzAEQ.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0"; + + /** + * Test that {@link HttpChallenge} generates a correct authorization key. + */ + @Test + public void testHttpChallenge() throws IOException { + KeyPair keypair = TestUtils.createKeyPair(); + Account account = new Account(keypair); + + HttpChallenge challenge = new HttpChallenge(); + challenge.unmarshall(TestUtils.getResourceAsJsonMap("/httpChallenge.json")); + + assertThat(challenge.getType(), is(HttpChallenge.TYPE)); + assertThat(challenge.getStatus(), is(Status.PENDING)); + + try { + challenge.getAuthorization(); + fail("getAuthorization() without previous authorize()"); + } catch (IllegalStateException ex) { + // expected + } + + challenge.authorize(account); + + assertThat(challenge.getToken(), is(TOKEN)); + assertThat(challenge.getAuthorization(), is(KEY_AUTHORIZATION)); + + ClaimBuilder cb = new ClaimBuilder(); + challenge.marshall(cb); + + assertThat(cb.toString(), sameJSONAs("{\"keyAuthorization\"=\"" + + KEY_AUTHORIZATION + "\"}").allowingExtraUnexpectedFields()); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java new file mode 100644 index 00000000..a73c01e8 --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java @@ -0,0 +1,456 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.connector; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.*; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.KeyPair; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.jose4j.base64url.Base64Url; +import org.jose4j.jwx.CompactSerializer; +import org.junit.Before; +import org.junit.Test; +import org.shredzone.acme4j.Account; +import org.shredzone.acme4j.exception.AcmeException; +import org.shredzone.acme4j.exception.AcmeServerException; +import org.shredzone.acme4j.provider.AcmeClientProvider; +import org.shredzone.acme4j.util.ClaimBuilder; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link Connection}. + * + * @author Richard "Shred" Körber + */ +public class ConnectionTest { + + private URI requestUri; + private AcmeClientProvider mockProvider; + private HttpURLConnection mockUrlConnection; + + @Before + public void setup() throws IOException, URISyntaxException { + requestUri = new URI("http://example.com/acme/"); + + mockUrlConnection = mock(HttpURLConnection.class); + + mockProvider = mock(AcmeClientProvider.class); + when(mockProvider.openConnection(requestUri)).thenReturn(mockUrlConnection); + } + + /** + * Test if {@link Connection#getNonceFromHeader(HttpURLConnection)} throws an + * exception if there is no {@code Replay-Nonce} header. + */ + @Test + public void testNoNonceFromHeader() throws AcmeException { + when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(null); + + try (Connection conn = new Connection(mockProvider)) { + conn.getNonceFromHeader(mockUrlConnection); + fail("Expected to fail"); + } catch (AcmeException ex) { + assertThat(ex.getMessage(), is("No replay nonce")); + } + + verify(mockUrlConnection).getHeaderField("Replay-Nonce"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that {@link Connection#getNonceFromHeader(HttpURLConnection)} extracts a + * {@code Replay-Nonce} header correctly. + */ + @Test + public void testGetNonceFromHeader() throws AcmeException { + byte[] nonce = "foo-nonce-foo".getBytes(); + + when(mockUrlConnection.getHeaderField("Replay-Nonce")) + .thenReturn(Base64Url.encode(nonce)); + + try (Connection conn = new Connection(mockProvider)) { + byte[] nonceFromHeader = conn.getNonceFromHeader(mockUrlConnection); + assertThat(nonceFromHeader, is(nonce)); + } + + verify(mockUrlConnection).getHeaderField("Replay-Nonce"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that {@link Connection#getNonceFromHeader(HttpURLConnection)} fails on an + * invalid {@code Replay-Nonce} header. + */ + @Test + public void testInvalidNonceFromHeader() throws AcmeException { + String badNonce = "#$%&/*+*#'"; + + when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(badNonce); + + try (Connection conn = new Connection(mockProvider)) { + conn.getNonceFromHeader(mockUrlConnection); + fail("Expected to fail"); + } catch (AcmeException ex) { + assertThat(ex.getMessage(), org.hamcrest.Matchers.startsWith("Invalid replay nonce")); + } + + verify(mockUrlConnection).getHeaderField("Replay-Nonce"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that a Location header is evaluated. + */ + @Test + public void testGetLocation() throws Exception { + when(mockUrlConnection.getHeaderField("Location")).thenReturn("http://example.com/otherlocation"); + + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + URI location = conn.getLocation(); + assertThat(location, is(new URI("http://example.com/otherlocation"))); + } + + verify(mockUrlConnection).getHeaderField("Location"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that no Location header returns {@code null}. + */ + @Test + public void testNoLocation() throws Exception { + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + URI location = conn.getLocation(); + assertThat(location, is(nullValue())); + } + + verify(mockUrlConnection).getHeaderField("Location"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that no exception is thrown if there is no problem. + */ + @Test + public void testNoThrowException() throws AcmeException { + when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/json"); + + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + conn.throwException(); + } + + verify(mockUrlConnection).getHeaderField("Content-Type"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test if an {@link AcmeServerException} is thrown on an acme problem. + */ + @Test + public void testThrowException() throws Exception { + String jsonData = "{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Invalid response: 404\"}"; + + when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/problem+json"); + when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN); + when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8"))); + + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + conn.throwException(); + fail("Expected to fail"); + } catch (AcmeServerException ex) { + assertThat(ex.getType(), is("urn:acme:error:unauthorized")); + assertThat(ex.getMessage(), is("Invalid response: 404")); + assertThat(ex.getAcmeErrorType(), is("unauthorized")); + } catch (AcmeException ex) { + fail("Expected an AcmeServerException"); + } + + verify(mockUrlConnection, atLeastOnce()).getHeaderField("Content-Type"); + verify(mockUrlConnection).getResponseCode(); + verify(mockUrlConnection).getErrorStream(); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test if an {@link AcmeServerException} is thrown on another problem. + */ + @Test + public void testOtherThrowException() { + when(mockUrlConnection.getHeaderField("Content-Type")) + .thenReturn("application/problem+json"); + + try (Connection conn = new Connection(mockProvider) { + @Override + public Map readJsonResponse() throws AcmeException { + Map result = new HashMap(); + result.put("type", "urn:zombie:error:apocalypse"); + result.put("detail", "Zombie apocalypse in progress"); + return result; + }; + }) { + conn.conn = mockUrlConnection; + conn.throwException(); + fail("Expected to fail"); + } catch (AcmeServerException ex) { + assertThat(ex.getType(), is("urn:zombie:error:apocalypse")); + assertThat(ex.getMessage(), is("Zombie apocalypse in progress")); + assertThat(ex.getAcmeErrorType(), is(nullValue())); + } catch (AcmeException ex) { + fail("Expected an AcmeServerException"); + } + + verify(mockUrlConnection).getHeaderField("Content-Type"); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that a session is properly started. + */ + @Test + public void testStartSession() throws Exception { + byte[] nonce = "foo-nonce-foo".getBytes(); + + when(mockUrlConnection.getHeaderField("Replay-Nonce")) + .thenReturn(Base64Url.encode(nonce)); + + Session session = new Session(); + try (Connection conn = new Connection(mockProvider)) { + conn.startSession(requestUri, session); + } + assertThat(session.getNonce(), is(nonce)); + + verify(mockUrlConnection).setRequestMethod("HEAD"); + verify(mockUrlConnection).connect(); + verify(mockUrlConnection).getHeaderField("Replay-Nonce"); + verify(mockProvider).openConnection(requestUri); + verifyNoMoreInteractions(mockUrlConnection); + verifyNoMoreInteractions(mockProvider); + } + + /** + * Test GET requests. + */ + @Test + public void testSendRequest() throws Exception { + final Set invoked = new HashSet<>(); + + try (Connection conn = new Connection(mockProvider) { + @Override + protected void throwException() throws AcmeException { + invoked.add("throwException"); + }; + }) { + conn.sendRequest(requestUri); + } + + verify(mockUrlConnection).setRequestMethod("GET"); + verify(mockUrlConnection).setRequestProperty("Accept-Charset", "utf-8"); + verify(mockUrlConnection).setDoOutput(false); + verify(mockUrlConnection).connect(); + verify(mockUrlConnection).getResponseCode(); + verify(mockProvider).openConnection(requestUri); + verifyNoMoreInteractions(mockUrlConnection); + verifyNoMoreInteractions(mockProvider); + assertThat(invoked, hasItem("throwException")); + } + + /** + * Test signed POST requests. + */ + @Test + public void testSendSignedRequest() throws Exception { + final byte[] nonce1 = "foo-nonce-1-foo".getBytes(); + final byte[] nonce2 = "foo-nonce-2-foo".getBytes(); + final Session testSession = new Session(); + final Set invoked = new HashSet<>(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + when(mockUrlConnection.getOutputStream()).thenReturn(outputStream); + when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(Base64Url.encode(nonce2)); + + try (Connection conn = new Connection(mockProvider) { + @Override + protected void throwException() throws AcmeException { + invoked.add("throwException"); + }; + @Override + public void startSession(URI uri, Session session) throws AcmeException { + assertThat(uri, is(requestUri)); + assertThat(session, is(sameInstance(testSession))); + assertThat(testSession.getNonce(), is(nullValue())); + invoked.add("startSession"); + session.setNonce(nonce1); + }; + }) { + ClaimBuilder cb = new ClaimBuilder(); + cb.put("foo", 123).put("bar", "a-string"); + + + KeyPair keypair = TestUtils.createKeyPair(); + Account account = new Account(keypair); + + conn.sendSignedRequest(requestUri, cb, testSession, account); + } + + verify(mockUrlConnection).setRequestMethod("POST"); + verify(mockUrlConnection).setRequestProperty("Accept", "application/json"); + verify(mockUrlConnection).setRequestProperty("Accept-Charset", "utf-8"); + verify(mockUrlConnection).setRequestProperty("Content-Type", "application/json"); + verify(mockUrlConnection).setDoOutput(true); + verify(mockUrlConnection).connect(); + verify(mockUrlConnection).setFixedLengthStreamingMode(outputStream.toByteArray().length); + verify(mockUrlConnection, atLeastOnce()).getHeaderField(anyString()); + verify(mockUrlConnection).getOutputStream(); + verify(mockUrlConnection).getResponseCode(); + verify(mockProvider).openConnection(requestUri); + verifyNoMoreInteractions(mockUrlConnection); + verifyNoMoreInteractions(mockProvider); + assertThat(invoked, hasItems("throwException", "startSession")); + + String[] written = CompactSerializer.deserialize(new String(outputStream.toByteArray(), "utf-8")); + String header = Base64Url.decodeToUtf8String(written[0]); + String claims = Base64Url.decodeToUtf8String(written[1]); + String signature = written[2]; + + StringBuilder expectedHeader = new StringBuilder(); + expectedHeader.append('{'); + expectedHeader.append("\"nonce\":\"").append(Base64Url.encode(nonce1)).append("\","); + expectedHeader.append("\"alg\":\"RS256\","); + expectedHeader.append("\"jwk\":{"); + expectedHeader.append("\"kty\":\"").append(TestUtils.KTY).append("\","); + expectedHeader.append("\"e\":\"").append(TestUtils.E).append("\","); + expectedHeader.append("\"n\":\"").append(TestUtils.N).append("\""); + expectedHeader.append("}}"); + + assertThat(header, sameJSONAs(expectedHeader.toString()).allowingExtraUnexpectedFields()); + assertThat(claims, sameJSONAs("{\"foo\":123,\"bar\":\"a-string\"}")); + assertThat(signature, not(isEmptyOrNullString())); + } + + /** + * Test getting a JSON response. + */ + @Test + public void testReadJsonResponse() throws Exception { + String jsonData = "{\"foo\":123,\"bar\":\"a-string\"}"; + + when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/json"); + when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8"))); + + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + Map result = conn.readJsonResponse(); + assertThat(result.keySet(), hasSize(2)); + assertThat(result, hasEntry("foo", (Object) 123L)); + assertThat(result, hasEntry("bar", (Object) "a-string")); + } + + verify(mockUrlConnection).getHeaderField("Content-Type"); + verify(mockUrlConnection).getResponseCode(); + verify(mockUrlConnection).getInputStream(); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test that a certificate is downloaded correctly. + */ + @Test + public void testReadCertificate() throws Exception { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + X509Certificate original; + try (InputStream cert = getClass().getResourceAsStream("/cert.pem")) { + original = (X509Certificate) certificateFactory.generateCertificate(cert); + } + + when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/pkix-cert"); + when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(original.getEncoded())); + + X509Certificate downloaded; + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + downloaded = conn.readCertificate(); + } + + assertThat(original, not(nullValue())); + assertThat(downloaded, not(nullValue())); + assertThat(original.getEncoded(), is(equalTo(downloaded.getEncoded()))); + + verify(mockUrlConnection).getHeaderField("Content-Type"); + verify(mockUrlConnection).getInputStream(); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + + /** + * Test if a resource directory is read correctly. + */ + @Test + public void testReadDirectory() throws Exception { + StringBuilder jsonData = new StringBuilder(); + jsonData.append('{'); + jsonData.append("\"new-reg\":\"http://example.com/acme/newreg\","); + jsonData.append("\"new-authz\":\"http://example.com/acme/newauthz\","); + jsonData.append("\"old-foo\":\"http://example.com/acme/oldfoo\""); + jsonData.append('}'); + + when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/json"); + when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(jsonData.toString().getBytes("utf-8"))); + + try (Connection conn = new Connection(mockProvider)) { + conn.conn = mockUrlConnection; + Map result = conn.readDirectory(); + assertThat(result.keySet(), hasSize(2)); + assertThat(result, hasEntry(Resource.NEW_REG, new URI("http://example.com/acme/newreg"))); + assertThat(result, hasEntry(Resource.NEW_AUTHZ, new URI("http://example.com/acme/newauthz"))); + // "old-foo" resource is unknown and thus not available in the map + } + + verify(mockUrlConnection).getHeaderField("Content-Type"); + verify(mockUrlConnection).getInputStream(); + verifyNoMoreInteractions(mockUrlConnection); + verifyZeroInteractions(mockProvider); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceTest.java new file mode 100644 index 00000000..5625bced --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceTest.java @@ -0,0 +1,56 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.connector; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +/** + * Unit test for {@link Resource}. + * + * @author Richard "Shred" Körber + */ +public class ResourceTest { + + /** + * Test {@link Resource#path()}. + */ + @Test + public void testPath() { + assertThat(Resource.NEW_AUTHZ.path(), is("new-authz")); + assertThat(Resource.NEW_CERT.path(), is("new-cert")); + assertThat(Resource.NEW_REG.path(), is("new-reg")); + + // fails if there are untested future Resource values + assertThat(Resource.values().length, is(3)); + } + + /** + * Test that invoking {@link Resource#parse(String)} with a {@link Resource#path()} + * gives the same {@link Resource}. + */ + @Test + public void testParse() { + for (Resource r : Resource.values()) { + Resource parsed = Resource.parse(r.path()); + assertThat(parsed, is(r)); + } + + // unknown paths return null + assertThat(Resource.parse("foo"), is(nullValue())); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionTest.java new file mode 100644 index 00000000..7a1d45d3 --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionTest.java @@ -0,0 +1,43 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.connector; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +/** + * Unit test for {@link Session}. + * + * @author Richard "Shred" Körber + */ +public class SessionTest { + + /** + * Test getters and setters. + */ + @Test + public void testGettersAndSetters() { + Session session = new Session(); + + assertThat(session.getNonce(), is(nullValue())); + + byte[] data = "foo-nonce-bar".getBytes(); + + session.setNonce(data); + assertThat(session.getNonce(), is(equalTo(data))); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/ClaimBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/ClaimBuilderTest.java index 1722aab1..0b992848 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/ClaimBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/ClaimBuilderTest.java @@ -16,8 +16,8 @@ package org.shredzone.acme4j.util; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; +import java.io.IOException; import java.security.KeyPair; -import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; @@ -118,10 +118,8 @@ public class ClaimBuilderTest { */ @Test @SuppressWarnings("unchecked") - public void testKey() throws NoSuchAlgorithmException, JoseException { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(512); - KeyPair keyPair = keyGen.generateKeyPair(); + public void testKey() throws IOException, NoSuchAlgorithmException, JoseException { + KeyPair keyPair = TestUtils.createKeyPair(); ClaimBuilder res; @@ -134,9 +132,9 @@ public class ClaimBuilderTest { Map jwk = (Map) json.get("foo"); assertThat(jwk.keySet(), hasSize(3)); - assertThat(jwk.get("n"), not(isEmptyOrNullString())); - assertThat(jwk, hasEntry("e", "AQAB")); - assertThat(jwk, hasEntry("kty", "RSA")); + assertThat(jwk, hasEntry("n", TestUtils.N)); + assertThat(jwk, hasEntry("e", TestUtils.E)); + assertThat(jwk, hasEntry("kty", TestUtils.KTY)); } /** diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java new file mode 100644 index 00000000..ab09460c --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java @@ -0,0 +1,165 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2015 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.util; + +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Map; +import java.util.TreeMap; + +import org.jose4j.base64url.Base64Url; +import org.jose4j.json.JsonUtil; +import org.jose4j.jwk.JsonWebKey; +import org.jose4j.jwk.JsonWebKey.OutputControlLevel; +import org.jose4j.lang.JoseException; + +/** + * Some utility methods for unit tests. + * + * @author Richard "Shred" Körber + */ +public final class TestUtils { + public static final String N = "pZsTKY41y_CwgJ0VX7BmmGs_7UprmXQMGPcnSbBeJAjZHA9SyyJKaWv4fNUdBIAX3Y2QoZixj50nQLyLv2ng3pvEoRL0sx9ZHgp5ndAjpIiVQ_8V01TTYCEDUc9ii7bjVkgFAb4ValZGFJZ54PcCnAHvXi5g0ELORzGcTuRqHVAUckMV2otr0g0u_5bWMm6EMAbBrGQCgUGjbZQHjava1Y-5tHXZkPBahJ2LvKRqMmJUlr0anKuJJtJUG03DJYAxABv8YAaXFBnGw6kKJRpUFAC55ry4sp4kGy0NrK2TVWmZW9kStniRv4RaJGI9aZGYwQy2kUykibBNmWEQUlIwIw"; + public static final String E = "AQAB"; + public static final String KTY = "RSA"; + public static final String THUMBPRINT = "HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0"; + + private TestUtils() { + // utility class without constructor + } + + /** + * Reads a resource as byte array. + * + * @param name + * Resource name + * @return Resource content as byte array. + */ + public static byte[] getResourceAsByteArray(String name) throws IOException { + byte[] buffer = new byte[2048]; + try (InputStream in = TestUtils.class.getResourceAsStream(name); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + int len; + while ((len = in.read(buffer)) >= 0) { + out.write(buffer, 0, len); + } + return out.toByteArray(); + } + } + + /** + * Reads a resource as String. + * + * @param name + * Resource name. The content is expected to be utf-8 encoded. + * @return Resource contents as string + */ + public static String getResourceAsString(String name) throws IOException { + try (InputStreamReader in = new InputStreamReader( + TestUtils.class.getResourceAsStream(name), "utf-8"); + StringWriter out = new StringWriter()) { + int ch; + while ((ch = in.read()) >= 0) { + out.write(ch); + } + return out.toString(); + } + } + + /** + * Reads a JSON resource and parses it. + * + * @param name + * Resource name of a utf-8 encoded JSON file. + * @return Parsed contents + */ + public static Map getResourceAsJsonMap(String name) throws IOException { + try { + return JsonUtil.parseJson(getResourceAsString(name)); + } catch (JoseException ex) { + throw new IOException("JSON error", ex); + } + } + + /** + * Creates a standard key pair for testing. This keypair is read from a test resource + * and is guaranteed not to change between test runs. + *

+ * The constants {@link #N}, {@link #E}, {@link #KTY} and {@link #THUMBPRINT} are + * related to the returned key pair and can be used for asserting results. + * + * @return {@link KeyPair} for testing + */ + public static KeyPair createKeyPair() throws IOException { + try { + KeyFactory keyFactory = KeyFactory.getInstance(KTY); + + X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( + getResourceAsByteArray("/public.key")); + PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); + + PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec( + getResourceAsByteArray("/private.key")); + PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); + + return new KeyPair(publicKey, privateKey); + } catch (NoSuchAlgorithmException | InvalidKeySpecException ex) { + throw new IOException(ex); + } + } + + /** + * Generates a new keypair for unit tests, and return its N, E, KTY and THUMBPRINT + * parameters to be set in the {@link TestUtils} class. + */ + public static void main(String... args) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(2048); + KeyPair keyPair = keyGen.generateKeyPair(); + + try (FileOutputStream out = new FileOutputStream("public.key")) { + out.write(keyPair.getPublic().getEncoded()); + } + + try (FileOutputStream out = new FileOutputStream("private.key")) { + out.write(keyPair.getPrivate().getEncoded()); + } + + final JsonWebKey jwk = JsonWebKey.Factory.newJwk(keyPair.getPublic()); + Map params = new TreeMap<>(jwk.toParams(OutputControlLevel.PUBLIC_ONLY)); + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(JsonUtil.toJson(params).getBytes("UTF-8")); + byte[] thumbprint = md.digest(); + + System.out.println("N = " + params.get("n")); + System.out.println("E = " + params.get("e")); + System.out.println("KTY = " + params.get("kty")); + System.out.println("THUMBPRINT = " + Base64Url.encode(thumbprint)); + } + +} diff --git a/acme4j-client/src/test/resources/cert.pem b/acme4j-client/src/test/resources/cert.pem new file mode 100644 index 00000000..6981323c --- /dev/null +++ b/acme4j-client/src/test/resources/cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVzCCAj+gAwIBAgIJAM4KDTzb0Y7NMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQwHhcNMTUxMjEwMDAxMTA4WhcNMjUxMjA3MDAxMTA4WjBCMQsw +CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh +dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +r0g3w4C8xbj/5lzJiDxk0HkEJeZeyruq+0AzOPMigJZ7zxZtX/KUxOIHrQ4qjcFh +l0DmQImoM0wESU+kcsjAHCx8E1lgRVlVsMfLAQPHkg5UybqfadzKT3ALcSD+9F9m +VIP6liC/6KzLTASmx6zM7j92KTl1ArObZr5mh0jvSNORrMhEC4Byn3+NTxjuHON1 +rWppCMwpeNNhFzaAig3O8PY8IyaLXNP2Ac5pXn0iW16S+Im9by7751UeW5a7Dznm +uMEM+WY640ffJDQ4+I64H403uAgvvSu+BGw8SEEZGuBCxoCnG1g6y6OvJyN5TgqF +dGosAfm1u+/MP1seoPdpBQIDAQABo1AwTjAdBgNVHQ4EFgQUrie5ZLOrA/HuhW1b +/CHjzEvj34swHwYDVR0jBBgwFoAUrie5ZLOrA/HuhW1b/CHjzEvj34swDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAkSOP0FUgIIUeJTObgXrenHzZpLAk +qXi37dgdYuPhNveo3agueP51N7yIoh6YGShiJ73Rvr+lVYTwFXStrLih1Wh3tWvk +sMxnvocgd7l6USRb5/AgH7eHeFK4DoCAak2hUAcCLDRJN3XMhNLpyJhw7GJxowVI +GUlxcW5Asrmh9qflfyMyjripTP3CdHobmNcNHyScjNncKj37m8vomel9acekTtDl +2Ci7nLdE+3VqQCXMIfLiF3PO0gGpKei0RuVCSOG6W83zVInCPd/l3aluSR+f/VZl +k8KGQ4As4uTQi89j+J1YepzG0ASMZpjVbXeIg5QBAywVxBh5XVTz37KN8A== +-----END CERTIFICATE----- diff --git a/acme4j-client/src/test/resources/dnsChallenge.json b/acme4j-client/src/test/resources/dnsChallenge.json new file mode 100644 index 00000000..e471ab93 --- /dev/null +++ b/acme4j-client/src/test/resources/dnsChallenge.json @@ -0,0 +1,5 @@ +{ + "type":"dns-01", + "status":"pending", + "token": "pNvmJivs0WCko2suV7fhe-59oFqyYx_yB7tx6kIMAyE" +} \ No newline at end of file diff --git a/acme4j-client/src/test/resources/genericChallenge.json b/acme4j-client/src/test/resources/genericChallenge.json new file mode 100644 index 00000000..f2e4dff6 --- /dev/null +++ b/acme4j-client/src/test/resources/genericChallenge.json @@ -0,0 +1,6 @@ +{ + "type":"generic-01", + "status":"valid", + "uri":"http://example.com/challenge/123", + "validated":"2015-12-12T17:19:36.336785823Z" +} \ No newline at end of file diff --git a/acme4j-client/src/test/resources/httpChallenge.json b/acme4j-client/src/test/resources/httpChallenge.json new file mode 100644 index 00000000..db705770 --- /dev/null +++ b/acme4j-client/src/test/resources/httpChallenge.json @@ -0,0 +1,5 @@ +{ + "type":"http-01", + "status":"pending", + "token": "rSoI9JpyvFi-ltdnBW0W1DjKstzG7cHixjzcOjwzAEQ" +} \ No newline at end of file diff --git a/acme4j-client/src/test/resources/private.key b/acme4j-client/src/test/resources/private.key new file mode 100644 index 00000000..1af94252 Binary files /dev/null and b/acme4j-client/src/test/resources/private.key differ diff --git a/acme4j-client/src/test/resources/public.key b/acme4j-client/src/test/resources/public.key new file mode 100644 index 00000000..22f94bb6 Binary files /dev/null and b/acme4j-client/src/test/resources/public.key differ diff --git a/pom.xml b/pom.xml index 6dacdf60..cfb89b16 100644 --- a/pom.xml +++ b/pom.xml @@ -164,6 +164,12 @@ [1.3,) test + + uk.co.datumedge + hamcrest-json + [0.1,) + test + org.mockito mockito-core