mirror of https://github.com/shred/acme4j
Add unit tests for acme4j-client
parent
1267684614
commit
7097f23a2d
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String,Object> readJsonResponse() throws AcmeException {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
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<String> 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<String> 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<String, Object> 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<Resource, URI> 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
|
@ -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<String, String> jwk = (Map<String, String>) 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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<String, Object> 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.
|
||||
* <p>
|
||||
* 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<String, Object> 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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-----
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type":"dns-01",
|
||||
"status":"pending",
|
||||
"token": "pNvmJivs0WCko2suV7fhe-59oFqyYx_yB7tx6kIMAyE"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type":"generic-01",
|
||||
"status":"valid",
|
||||
"uri":"http://example.com/challenge/123",
|
||||
"validated":"2015-12-12T17:19:36.336785823Z"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type":"http-01",
|
||||
"status":"pending",
|
||||
"token": "rSoI9JpyvFi-ltdnBW0W1DjKstzG7cHixjzcOjwzAEQ"
|
||||
}
|
Binary file not shown.
Binary file not shown.
6
pom.xml
6
pom.xml
|
@ -164,6 +164,12 @@
|
|||
<version>[1.3,)</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>uk.co.datumedge</groupId>
|
||||
<artifactId>hamcrest-json</artifactId>
|
||||
<version>[0.1,)</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
|
|
Loading…
Reference in New Issue