Challenge constructor requires JSON data

pull/55/head
Richard Körber 2018-01-14 14:16:20 +01:00
parent 252d886b3f
commit 8923e35d21
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
20 changed files with 153 additions and 121 deletions

View File

@ -176,17 +176,14 @@ public class Session {
public Challenge createChallenge(JSON data) {
Objects.requireNonNull(data, "data");
String type = data.get("type").required().asString();
Challenge challenge = provider().createChallenge(this, type);
Challenge challenge = provider().createChallenge(this, data);
if (challenge == null) {
if (data.contains("token")) {
challenge = new TokenChallenge(this);
challenge = new TokenChallenge(this, data);
} else {
challenge = new Challenge(this);
challenge = new Challenge(this, data);
}
}
challenge.setJSON(data);
return challenge;
}

View File

@ -58,9 +58,11 @@ public class Challenge extends AcmeJsonResource {
*
* @param session
* {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/
public Challenge(Session session) {
super(session);
public Challenge(Session session, JSON data) {
super(session, data);
}
/**
@ -159,16 +161,14 @@ public class Challenge extends AcmeJsonResource {
}
@Override
public void setJSON(JSON json) {
String type = json.get(KEY_TYPE).asString();
if (type == null) {
throw new IllegalArgumentException("map does not contain a type");
}
protected void setJSON(JSON json) {
String type = json.get(KEY_TYPE).required().asString();
if (!acceptable(type)) {
throw new AcmeProtocolException("wrong type: " + type);
throw new AcmeProtocolException("incompatible type " + type + " for this challenge");
}
setLocation(json.get(KEY_URL).asURL());
setLocation(json.get(KEY_URL).required().asURL());
super.setJSON(json);
}

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge.
@ -33,9 +34,11 @@ public class Dns01Challenge extends TokenChallenge {
*
* @param session
* {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/
public Dns01Challenge(Session session) {
super(session);
public Dns01Challenge(Session session, JSON data) {
super(session, data);
}
/**

View File

@ -14,6 +14,7 @@
package org.shredzone.acme4j.challenge;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge.
@ -31,9 +32,11 @@ public class Http01Challenge extends TokenChallenge {
*
* @param session
* {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/
public Http01Challenge(Session session) {
super(session);
public Http01Challenge(Session session, JSON data) {
super(session, data);
}
/**

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.toolbox.JSON;
/**
* Implements the {@value TYPE} challenge.
@ -33,9 +34,11 @@ public class TlsSni02Challenge extends TokenChallenge {
*
* @param session
* {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/
public TlsSni02Challenge(Session session) {
super(session);
public TlsSni02Challenge(Session session, JSON data) {
super(session, data);
}
/**

View File

@ -21,6 +21,7 @@ import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
/**
@ -38,9 +39,11 @@ public class TokenChallenge extends Challenge {
*
* @param session
* {@link Session} to bind to.
* @param data
* {@link JSON} challenge data
*/
public TokenChallenge(Session session) {
super(session);
public TokenChallenge(Session session, JSON data) {
super(session, data);
}
@Override

View File

@ -18,7 +18,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.BiFunction;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
@ -40,7 +40,7 @@ import org.shredzone.acme4j.toolbox.JSON;
*/
public abstract class AbstractAcmeProvider implements AcmeProvider {
private static final Map<String, Function<Session, Challenge>> CHALLENGES = challengeMap();
private static final Map<String, BiFunction<Session, JSON, Challenge>> CHALLENGES = challengeMap();
@Override
public Connection connect() {
@ -59,8 +59,8 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
}
}
private static Map<String, Function<Session, Challenge>> challengeMap() {
Map<String, Function<Session, Challenge>> map = new HashMap<>();
private static Map<String, BiFunction<Session, JSON, Challenge>> challengeMap() {
Map<String, BiFunction<Session, JSON, Challenge>> map = new HashMap<>();
map.put(Dns01Challenge.TYPE, Dns01Challenge::new);
map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);
@ -69,17 +69,25 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
return Collections.unmodifiableMap(map);
}
/**
* {@inheritDoc}
* <p>
* Custom provider implementations may override this method to provide challenges that
* are unique to the provider.
*/
@Override
public Challenge createChallenge(Session session, String type) {
public Challenge createChallenge(Session session, JSON data) {
Objects.requireNonNull(session, "session");
Objects.requireNonNull(type, "type");
Objects.requireNonNull(data, "data");
Function<Session, Challenge> constructor = CHALLENGES.get(type);
String type = data.get("type").required().asString();
BiFunction<Session, JSON, Challenge> constructor = CHALLENGES.get(type);
if (constructor == null) {
return null;
}
return constructor.apply(session);
return constructor.apply(session, data);
}
/**

View File

@ -75,17 +75,15 @@ public interface AcmeProvider {
JSON directory(Session session, URI serverUri) throws AcmeException;
/**
* Creates a {@link Challenge} instance for the given challenge type.
* <p>
* Custom provider implementations may override this method to provide challenges that
* are unique to the provider.
* Creates a {@link Challenge} instance for the given challenge data.
*
* @param session
* {@link Session} to bind the challenge to
* @param type
* Challenge type
* @return {@link Challenge} instance
* @param data
* Challenge {@link JSON} data
* @return {@link Challenge} instance, or {@code null} if this provider is unable to
* generate a matching {@link Challenge} instance.
*/
Challenge createChallenge(Session session, String type);
Challenge createChallenge(Session session, JSON data);
}

View File

@ -35,7 +35,6 @@ import org.jose4j.jwx.CompactSerializer;
import org.jose4j.lang.JoseException;
import org.junit.Test;
import org.shredzone.acme4j.Account.EditableAccount;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.connector.Resource;
@ -196,12 +195,9 @@ public class AccountTest {
Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(session);
provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl);
provider.putTestChallenge(Http01Challenge.TYPE, httpChallenge);
provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge);
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
provider.putTestChallenge(Dns01Challenge.TYPE, Dns01Challenge::new);
String domainName = "example.org";
@ -214,7 +210,8 @@ public class AccountTest {
assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge));
provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close();
}

View File

@ -108,10 +108,8 @@ public class AuthorizationTest {
Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(session);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
auth.update();
@ -122,7 +120,8 @@ public class AuthorizationTest {
assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge));
provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close();
}
@ -154,8 +153,8 @@ public class AuthorizationTest {
Session session = provider.createSession();
provider.putTestChallenge("http-01", new Http01Challenge(session));
provider.putTestChallenge("dns-01", new Dns01Challenge(session));
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
@ -200,10 +199,8 @@ public class AuthorizationTest {
Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(session);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
@ -220,7 +217,8 @@ public class AuthorizationTest {
assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge));
provider.getChallenge(Http01Challenge.TYPE),
provider.getChallenge(Dns01Challenge.TYPE)));
provider.close();
}
@ -249,10 +247,8 @@ public class AuthorizationTest {
Session session = provider.createSession();
Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(session);
provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge);
provider.putTestChallenge("http-01", Http01Challenge::new);
provider.putTestChallenge("dns-01", Dns01Challenge::new);
Authorization auth = new Authorization(session, locationUrl);
auth.deactivate();
@ -267,10 +263,10 @@ public class AuthorizationTest {
try (TestableConnectionProvider provider = new TestableConnectionProvider()) {
Session session = provider.createSession();
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session));
provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session));
provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session));
provider.putTestChallenge(DUPLICATE_TYPE, new Challenge(session));
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
provider.putTestChallenge(Dns01Challenge.TYPE, Dns01Challenge::new);
provider.putTestChallenge(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);
provider.putTestChallenge(DUPLICATE_TYPE, Challenge::new);
Authorization authorization = new Authorization(session, locationUrl);
authorization.setJSON(getJSON("authorizationChallenges"));

View File

@ -125,9 +125,11 @@ public class SessionTest {
KeyPair keyPair = TestUtils.createKeyPair();
URI serverUri = URI.create(TestUtils.ACME_SERVER_URI);
String challengeType = Http01Challenge.TYPE;
URL challengeUrl = new URL("https://example.com/acme/authz/0");
JSON data = new JSONBuilder()
.put("type", challengeType)
.put("url", challengeUrl)
.toJSON();
Http01Challenge mockChallenge = mock(Http01Challenge.class);
@ -135,7 +137,7 @@ public class SessionTest {
when(mockProvider.createChallenge(
ArgumentMatchers.any(Session.class),
ArgumentMatchers.eq(challengeType)))
ArgumentMatchers.eq(data)))
.thenReturn(mockChallenge);
Session session = new Session(serverUri, keyPair) {
@ -149,7 +151,7 @@ public class SessionTest {
assertThat(challenge, is(instanceOf(Http01Challenge.class)));
assertThat(challenge, is(sameInstance((Challenge) mockChallenge)));
verify(mockProvider).createChallenge(session, challengeType);
verify(mockProvider).createChallenge(session, data);
}
/**

View File

@ -47,7 +47,6 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/
public class ChallengeTest {
private Session session;
private URL resourceUrl = url("https://example.com/acme/some-resource");
private URL locationUrl = url("https://example.com/acme/some-location");
@Before
@ -74,7 +73,7 @@ public class ChallengeTest {
Session session = provider.createSession();
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session));
provider.putTestChallenge(Http01Challenge.TYPE, Http01Challenge::new);
Http01Challenge challenge = Challenge.bind(session, locationUrl);
@ -91,8 +90,7 @@ public class ChallengeTest {
*/
@Test
public void testUnmarshal() throws URISyntaxException {
Challenge challenge = new Challenge(session);
challenge.setJSON(getJSON("genericChallenge"));
Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
// Test unmarshalled values
assertThat(challenge.getType(), is("generic-01"));
@ -126,8 +124,7 @@ public class ChallengeTest {
*/
@Test
public void testRespond() throws JoseException {
Challenge challenge = new Challenge(session);
challenge.setJSON(getJSON("genericChallenge"));
Challenge challenge = new Challenge(session, getJSON("genericChallenge"));
JSONBuilder cb = new JSONBuilder();
challenge.respond(cb);
@ -140,8 +137,7 @@ public class ChallengeTest {
*/
@Test(expected = AcmeProtocolException.class)
public void testNotAcceptable() throws URISyntaxException {
Http01Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("dnsChallenge"));
new Http01Challenge(session, getJSON("dnsChallenge"));
}
/**
@ -152,7 +148,7 @@ public class ChallengeTest {
TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override
public int sendSignedRequest(URL url, JSONBuilder claims, Session session, int... httpStatus) {
assertThat(url, is(resourceUrl));
assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("triggerHttpChallengeRequest").toString()));
assertThat(session, is(notNullValue()));
assertThat(httpStatus, isIntArrayContainingInAnyOrder());
@ -167,8 +163,7 @@ public class ChallengeTest {
Session session = provider.createSession();
Http01Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("triggerHttpChallenge"));
Http01Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallenge"));
challenge.trigger();
@ -202,8 +197,7 @@ public class ChallengeTest {
Session session = provider.createSession();
Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("triggerHttpChallengeResponse"));
Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
challenge.update();
@ -240,8 +234,7 @@ public class ChallengeTest {
Session session = provider.createSession();
Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("triggerHttpChallengeResponse"));
Challenge challenge = new Http01Challenge(session, getJSON("triggerHttpChallengeResponse"));
try {
challenge.update();
@ -302,10 +295,9 @@ public class ChallengeTest {
/**
* Test that unmarshalling something different like a challenge fails.
*/
@Test(expected = IllegalArgumentException.class)
@Test(expected = AcmeProtocolException.class)
public void testBadUnmarshall() {
Challenge challenge = new Challenge(session);
challenge.setJSON(getJSON("updateAccountResponse"));
new Challenge(session, getJSON("updateAccountResponse"));
}
}

View File

@ -46,8 +46,7 @@ public class DnsChallengeTest {
*/
@Test
public void testDnsChallenge() throws IOException {
Dns01Challenge challenge = new Dns01Challenge(session);
challenge.setJSON(getJSON("dnsChallenge"));
Dns01Challenge challenge = new Dns01Challenge(session, getJSON("dnsChallenge"));
assertThat(challenge.getType(), is(Dns01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));

View File

@ -49,8 +49,7 @@ public class HttpChallengeTest {
*/
@Test
public void testHttpChallenge() throws IOException {
Http01Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("httpChallenge"));
Http01Challenge challenge = new Http01Challenge(session, getJSON("httpChallenge"));
assertThat(challenge.getType(), is(Http01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));
@ -69,8 +68,7 @@ public class HttpChallengeTest {
*/
@Test(expected = AcmeProtocolException.class)
public void testNoTokenSet() {
Http01Challenge challenge = new Http01Challenge(session);
challenge.setJSON(getJSON("httpNoTokenChallenge"));
Http01Challenge challenge = new Http01Challenge(session, getJSON("httpNoTokenChallenge"));
challenge.getToken();
}

View File

@ -46,8 +46,7 @@ public class TlsSni02ChallengeTest {
*/
@Test
public void testTlsSni02Challenge() throws IOException {
TlsSni02Challenge challenge = new TlsSni02Challenge(session);
challenge.setJSON(getJSON("tlsSni02Challenge"));
TlsSni02Challenge challenge = new TlsSni02Challenge(session, getJSON("tlsSni02Challenge"));
assertThat(challenge.getType(), is(TlsSni02Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));

View File

@ -103,7 +103,7 @@ public class SessionProviderTest {
}
@Override
public Challenge createChallenge(Session session, String type) {
public Challenge createChallenge(Session session, JSON data) {
throw new UnsupportedOperationException();
}
}
@ -131,7 +131,7 @@ public class SessionProviderTest {
}
@Override
public Challenge createChallenge(Session session, String type) {
public Challenge createChallenge(Session session, JSON data) {
throw new UnsupportedOperationException();
}
}

View File

@ -34,7 +34,9 @@ import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
@ -133,29 +135,40 @@ public class AbstractAcmeProviderTest {
}
};
Challenge c1 = provider.createChallenge(session, Http01Challenge.TYPE);
Challenge c1 = provider.createChallenge(session, getJSON("httpChallenge"));
assertThat(c1, not(nullValue()));
assertThat(c1, instanceOf(Http01Challenge.class));
Challenge c2 = provider.createChallenge(session, Http01Challenge.TYPE);
Challenge c2 = provider.createChallenge(session, getJSON("httpChallenge"));
assertThat(c2, not(sameInstance(c1)));
Challenge c3 = provider.createChallenge(session, Dns01Challenge.TYPE);
Challenge c3 = provider.createChallenge(session, getJSON("dnsChallenge"));
assertThat(c3, not(nullValue()));
assertThat(c3, instanceOf(Dns01Challenge.class));
Challenge c5 = provider.createChallenge(session, TlsSni02Challenge.TYPE);
Challenge c5 = provider.createChallenge(session, getJSON("tlsSni02Challenge"));
assertThat(c5, not(nullValue()));
assertThat(c5, instanceOf(TlsSni02Challenge.class));
Challenge c6 = provider.createChallenge(session, "foobar-01");
JSON json6 = new JSONBuilder()
.put("type", "foobar-01")
.put("url", "https://example.com/some/challenge")
.toJSON();
Challenge c6 = provider.createChallenge(session, json6);
assertThat(c6, is(nullValue()));
Challenge c7 = provider.createChallenge(session, "");
assertThat(c7, is(nullValue()));
try {
JSON json7 = new JSONBuilder()
.put("url", "https://example.com/some/challenge")
.toJSON();
provider.createChallenge(session, json7);
fail("Challenge without type was accepted");
} catch (AcmeProtocolException ex) {
// expected
}
try {
provider.createChallenge(session, (String) null);
provider.createChallenge(session, null);
fail("null was accepted");
} catch (NullPointerException ex) {
// expected

View File

@ -18,6 +18,7 @@ import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
@ -34,7 +35,8 @@ import org.shredzone.acme4j.toolbox.TestUtils;
* of {@link Connection} that is always returned on {@link #connect()}.
*/
public class TestableConnectionProvider extends DummyConnection implements AcmeProvider {
private final Map<String, Challenge> challengeMap = new HashMap<>();
private final Map<String, BiFunction<Session, JSON, Challenge>> creatorMap = new HashMap<>();
private final Map<String, Challenge> createdMap = new HashMap<>();
private final JSONBuilder directory = new JSONBuilder();
/**
@ -50,17 +52,29 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
}
/**
* Register a {@link Challenge}. For the sake of simplicity,
* {@link #createChallenge(Session, String)} will always return the same
* {@link Challenge} instance in this test suite.
* Register a {@link Challenge}.
*
* @param s
* Challenge type
* @param c
* {@link Challenge} instance.
* @param type
* Challenge type to register.
* @param creator
* Creator {@link BiFunction} that creates a matching {@link Challenge}
*/
public void putTestChallenge(String s, Challenge c) {
challengeMap.put(s, c);
public void putTestChallenge(String type, BiFunction<Session, JSON, Challenge> creator) {
creatorMap.put(type, creator);
}
/**
* Returns the {@link Challenge} instance that has been created. Fails if no such
* challenge was created.
*
* @param type Challenge type
* @return Created {@link Challenge} instance
*/
public Challenge getChallenge(String type) {
if (!createdMap.containsKey(type)) {
throw new IllegalArgumentException("No challenge of type " + type + " was created");
}
return createdMap.get(type);
}
/**
@ -94,16 +108,23 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
}
@Override
public Challenge createChallenge(Session session, String type) {
if (challengeMap.isEmpty()) {
public Challenge createChallenge(Session session, JSON data) {
if (creatorMap.isEmpty()) {
throw new UnsupportedOperationException();
}
if (challengeMap.containsKey(type)) {
return challengeMap.get(type);
Challenge created;
String type = data.get("type").asString();
if (creatorMap.containsKey(type)) {
created = creatorMap.get(type).apply(session, data);
} else {
return new Challenge(session);
created = new Challenge(session, data);
}
createdMap.put(type, created);
return created;
}
}

View File

@ -1,6 +1,6 @@
{
"type": "http-01",
"status": "pending",
"url": "https://example.com/acme/some-resource",
"url": "https://example.com/acme/some-location",
"token": "IlirfxKKXAsHtmzK29Pj8A"
}

View File

@ -37,7 +37,7 @@ The standard Java mechanisms are used to verify the HTTPS certificate provided b
If your ACME server provides challenges that are not specified in the ACME protocol, there should be an own `Challenge` implementation for each of your challenge, by extending the [`Challenge`](../apidocs/org/shredzone/acme4j/challenge/Challenge.html) class.
In your `AcmeProvider` implementation, override the `createChallenge(Session, String)` method so it returns a new instance of your `Challenge` implementation when your individual challenge type is requested. All other types should be delegated to the super method.
In your `AcmeProvider` implementation, override the `createChallenge(Session, JSON)` method so it returns a new instance of your `Challenge` implementation when your individual challenge type is requested. All other types should be delegated to the super method.
## Amended Directory Service