mirror of https://github.com/shred/acme4j
Permit to globally register additional challenge types
parent
24af42b6b9
commit
2f2e59fd36
|
@ -25,6 +25,7 @@ module org.shredzone.acme4j {
|
|||
exports org.shredzone.acme4j.toolbox;
|
||||
|
||||
uses org.shredzone.acme4j.provider.AcmeProvider;
|
||||
uses org.shredzone.acme4j.provider.ChallengeProvider;
|
||||
|
||||
provides org.shredzone.acme4j.provider.AcmeProvider
|
||||
with org.shredzone.acme4j.provider.GenericAcmeProvider,
|
||||
|
|
|
@ -56,14 +56,30 @@ public class TokenChallenge extends Challenge {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization string.
|
||||
* Computes the key authorization for the given token.
|
||||
* <p>
|
||||
* The default is {@code token + '.' + base64url(jwkThumbprint)}. Subclasses may
|
||||
* override this method if a different algorithm is used.
|
||||
*
|
||||
* @param token
|
||||
* Token to be used
|
||||
* @return Key Authorization string for that token
|
||||
* @since 2.12
|
||||
*/
|
||||
protected String keyAuthorizationFor(String token) {
|
||||
PublicKey pk = getLogin().getKeyPair().getPublic();
|
||||
return token + '.' + base64UrlEncode(JoseUtils.thumbprint(pk));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization string.
|
||||
* <p>
|
||||
* The default uses {@link #keyAuthorizationFor(String)} to compute the key
|
||||
* authorization of {@link #getToken()}. Subclasses may override this method if a
|
||||
* different algorithm is used.
|
||||
*/
|
||||
public String getAuthorization() {
|
||||
PublicKey pk = getLogin().getKeyPair().getPublic();
|
||||
return getToken() + '.' + base64UrlEncode(JoseUtils.thumbprint(pk));
|
||||
return keyAuthorizationFor(getToken());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import org.shredzone.acme4j.Login;
|
||||
import org.shredzone.acme4j.Session;
|
||||
|
@ -44,7 +44,7 @@ import org.shredzone.acme4j.toolbox.JSON;
|
|||
*/
|
||||
public abstract class AbstractAcmeProvider implements AcmeProvider {
|
||||
|
||||
private static final Map<String, BiFunction<Login, JSON, Challenge>> CHALLENGES = challengeMap();
|
||||
private static final Map<String, ChallengeProvider> CHALLENGES = challengeMap();
|
||||
|
||||
@Override
|
||||
public Connection connect(URI serverUri) {
|
||||
|
@ -81,13 +81,35 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private static Map<String, BiFunction<Login, JSON, Challenge>> challengeMap() {
|
||||
Map<String, BiFunction<Login, JSON, Challenge>> map = new HashMap<>();
|
||||
private static Map<String, ChallengeProvider> challengeMap() {
|
||||
Map<String, ChallengeProvider> map = new HashMap<>();
|
||||
|
||||
map.put(Dns01Challenge.TYPE, Dns01Challenge::new);
|
||||
map.put(Http01Challenge.TYPE, Http01Challenge::new);
|
||||
map.put(TlsAlpn01Challenge.TYPE, TlsAlpn01Challenge::new);
|
||||
|
||||
for (ChallengeProvider provider : ServiceLoader.load(ChallengeProvider.class)) {
|
||||
ChallengeType typeAnno = provider.getClass().getAnnotation(ChallengeType.class);
|
||||
if (typeAnno == null) {
|
||||
throw new IllegalStateException("ChallengeProvider "
|
||||
+ provider.getClass().getName()
|
||||
+ " has no @ChallengeType annotation");
|
||||
}
|
||||
String type = typeAnno.value();
|
||||
if (type == null || type.trim().isEmpty()) {
|
||||
throw new IllegalStateException("ChallengeProvider "
|
||||
+ provider.getClass().getName()
|
||||
+ ": type must not be null or empty");
|
||||
}
|
||||
if (map.containsKey(type)) {
|
||||
throw new IllegalStateException("ChallengeProvider "
|
||||
+ provider.getClass().getName()
|
||||
+ ": there is already a provider for challenge type "
|
||||
+ type);
|
||||
}
|
||||
map.put(type, provider);
|
||||
}
|
||||
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
|
@ -107,9 +129,9 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
|
|||
|
||||
String type = data.get("type").asString();
|
||||
|
||||
BiFunction<Login, JSON, Challenge> constructor = CHALLENGES.get(type);
|
||||
ChallengeProvider constructor = CHALLENGES.get(type);
|
||||
if (constructor != null) {
|
||||
return constructor.apply(login, data);
|
||||
return constructor.create(login, data);
|
||||
}
|
||||
|
||||
if (data.contains("token")) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* acme4j - Java ACME client
|
||||
*
|
||||
* Copyright (C) 2021 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.provider;
|
||||
|
||||
import org.shredzone.acme4j.Login;
|
||||
import org.shredzone.acme4j.challenge.Challenge;
|
||||
import org.shredzone.acme4j.toolbox.JSON;
|
||||
|
||||
/**
|
||||
* A provider that creates a Challenge from a matching JSON.
|
||||
*
|
||||
* @since 2.12
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ChallengeProvider {
|
||||
|
||||
/**
|
||||
* Creates a Challenge.
|
||||
*
|
||||
* @param login
|
||||
* {@link Login} of the user's account
|
||||
* @param data
|
||||
* {@link JSON} of the challenge as sent by the CA
|
||||
* @return Created and initialized {@link Challenge}. It must match the JSON type.
|
||||
*/
|
||||
Challenge create(Login login, JSON data);
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* acme4j - Java ACME client
|
||||
*
|
||||
* Copyright (C) 2021 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.provider;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotates the challenge type that is generated by the {@link ChallengeProvider}.
|
||||
*
|
||||
* @since 2.12
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface ChallengeType {
|
||||
|
||||
/**
|
||||
* Challenge type.
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
Loading…
Reference in New Issue