mirror of https://github.com/shred/acme4j
Add a GenericTokenChallenge. Remove boilerplate code.
parent
ade0207d6d
commit
9b458fb2b6
|
@ -18,15 +18,13 @@ import java.security.MessageDigest;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.shredzone.acme4j.Account;
|
||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||
|
||||
/**
|
||||
* Implements the {@code dns-01} challenge.
|
||||
*
|
||||
* @author Richard "Shred" Körber
|
||||
*/
|
||||
public class DnsChallenge extends GenericChallenge {
|
||||
public class DnsChallenge extends GenericTokenChallenge {
|
||||
private static final long serialVersionUID = 6964687027713533075L;
|
||||
|
||||
/**
|
||||
|
@ -34,27 +32,13 @@ public class DnsChallenge extends GenericChallenge {
|
|||
*/
|
||||
public static final String TYPE = "dns-01";
|
||||
|
||||
private String authorization = null;
|
||||
|
||||
/**
|
||||
* Authorizes the {@link Challenge} by signing it with an {@link Account}.
|
||||
*
|
||||
* @param account
|
||||
* {@link Account} to sign the challenge with
|
||||
*/
|
||||
public void authorize(Account account) {
|
||||
if (account == null) {
|
||||
throw new NullPointerException("account must not be null");
|
||||
}
|
||||
|
||||
authorization = getToken() + '.' + Base64Url.encode(jwkThumbprint(account.getKeyPair().getPublic()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the digest string to be set in the domain's {@code _acme-challenge} TXT
|
||||
* record.
|
||||
*/
|
||||
public String getDigest() {
|
||||
assertIsAuthorized();
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(getAuthorization().getBytes("UTF-8"));
|
||||
|
@ -66,32 +50,9 @@ public class DnsChallenge extends GenericChallenge {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respond(ClaimBuilder cb) {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
|
||||
super.respond(cb);
|
||||
cb.put(KEY_TOKEN, getToken());
|
||||
cb.put(KEY_KEY_AUTHORIZATION, getAuthorization());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean acceptable(String type) {
|
||||
return TYPE.equals(type);
|
||||
}
|
||||
|
||||
private String getToken() {
|
||||
return get(KEY_TOKEN);
|
||||
}
|
||||
|
||||
private String getAuthorization() {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -50,8 +50,6 @@ public class GenericChallenge implements Challenge {
|
|||
protected static final String KEY_STATUS = "status";
|
||||
protected static final String KEY_URI = "uri";
|
||||
protected static final String KEY_VALIDATED = "validated";
|
||||
protected static final String KEY_TOKEN = "token";
|
||||
protected static final String KEY_KEY_AUTHORIZATION = "keyAuthorization";
|
||||
|
||||
private transient Map<String, Object> data = new HashMap<>();
|
||||
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 org.jose4j.base64url.Base64Url;
|
||||
import org.shredzone.acme4j.Account;
|
||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||
|
||||
/**
|
||||
* An extension of {@link GenericChallenge} that handles challenges with a {@code token}
|
||||
* and {@code keyAuthorization}.
|
||||
*
|
||||
* @author Richard "Shred" Körber
|
||||
*/
|
||||
public class GenericTokenChallenge extends GenericChallenge {
|
||||
private static final long serialVersionUID = 1634133407432681800L;
|
||||
|
||||
protected static final String KEY_TOKEN = "token";
|
||||
protected static final String KEY_KEY_AUTHORIZATION = "keyAuthorization";
|
||||
|
||||
private String authorization;
|
||||
|
||||
/**
|
||||
* Authorizes the {@link Challenge} by signing it with an {@link Account}.
|
||||
*
|
||||
* @param account
|
||||
* {@link Account} to sign the challenge with
|
||||
*/
|
||||
public void authorize(Account account) {
|
||||
if (account == null) {
|
||||
throw new NullPointerException("account must not be null");
|
||||
}
|
||||
|
||||
authorization = computeAuthorization(account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respond(ClaimBuilder cb) {
|
||||
assertIsAuthorized();
|
||||
|
||||
super.respond(cb);
|
||||
cb.put(KEY_TOKEN, getToken());
|
||||
cb.put(KEY_KEY_AUTHORIZATION, getAuthorization());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the challenge was authorized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if {@link #authorize(Account)} was not invoked.
|
||||
*/
|
||||
protected void assertIsAuthorized() {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token.
|
||||
*/
|
||||
protected String getToken() {
|
||||
return get(KEY_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the authorization after {@link #authorize(Account)} was invoked.
|
||||
*/
|
||||
protected String getAuthorization() {
|
||||
assertIsAuthorized();
|
||||
return authorization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the authorization string.
|
||||
* <p>
|
||||
* The default is {@code token + '.' + base64url(jwkThumbprint)}. Subclasses may
|
||||
* override this method if a different algorithm is used.
|
||||
*
|
||||
* @param account
|
||||
* {@link Account} to authorize with
|
||||
* @return Authorization string
|
||||
*/
|
||||
protected String computeAuthorization(Account account) {
|
||||
return getToken()
|
||||
+ '.'
|
||||
+ Base64Url.encode(jwkThumbprint(account.getKeyPair().getPublic()));
|
||||
}
|
||||
|
||||
}
|
|
@ -13,16 +13,13 @@
|
|||
*/
|
||||
package org.shredzone.acme4j.challenge;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.shredzone.acme4j.Account;
|
||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||
|
||||
/**
|
||||
* Implements the {@code http-01} challenge.
|
||||
*
|
||||
* @author Richard "Shred" Körber
|
||||
*/
|
||||
public class HttpChallenge extends GenericChallenge {
|
||||
public class HttpChallenge extends GenericTokenChallenge {
|
||||
private static final long serialVersionUID = 3322211185872544605L;
|
||||
|
||||
/**
|
||||
|
@ -30,27 +27,12 @@ public class HttpChallenge extends GenericChallenge {
|
|||
*/
|
||||
public static final String TYPE = "http-01";
|
||||
|
||||
private String authorization = null;
|
||||
|
||||
/**
|
||||
* Authorizes the {@link Challenge} by signing it with an {@link Account}.
|
||||
*
|
||||
* @param account
|
||||
* {@link Account} to sign the challenge with
|
||||
*/
|
||||
public void authorize(Account account) {
|
||||
if (account == null) {
|
||||
throw new NullPointerException("account must not be null");
|
||||
}
|
||||
|
||||
authorization = getToken() + '.' + Base64Url.encode(jwkThumbprint(account.getKeyPair().getPublic()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token to be used for this challenge.
|
||||
*/
|
||||
@Override
|
||||
public String getToken() {
|
||||
return get(KEY_TOKEN);
|
||||
return super.getToken();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,22 +42,9 @@ public class HttpChallenge extends GenericChallenge {
|
|||
* or ASCII encoded). There must not be any other leading or trailing characters
|
||||
* (like white-spaces or line breaks). Otherwise the challenge will fail.
|
||||
*/
|
||||
public String getAuthorization() {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
return authorization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respond(ClaimBuilder cb) {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
|
||||
super.respond(cb);
|
||||
cb.put(KEY_TOKEN, getToken());
|
||||
cb.put(KEY_KEY_AUTHORIZATION, getAuthorization());
|
||||
public String getAuthorization() {
|
||||
return super.getAuthorization();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -40,6 +40,9 @@ import org.shredzone.acme4j.util.ValidationBuilder;
|
|||
public class ProofOfPossessionChallenge extends GenericChallenge {
|
||||
private static final long serialVersionUID = 6212440828380185335L;
|
||||
|
||||
protected static final String KEY_CERTS = "certs";
|
||||
protected static final String KEY_AUTHORIZATION = "authorization";
|
||||
|
||||
/**
|
||||
* Challenge type name: {@value}
|
||||
*/
|
||||
|
@ -96,7 +99,7 @@ public class ProofOfPossessionChallenge extends GenericChallenge {
|
|||
public void unmarshall(Map<String, Object> map) {
|
||||
super.unmarshall(map);
|
||||
|
||||
List<String> certData = get("certs");
|
||||
List<String> certData = get(KEY_CERTS);
|
||||
if (certData != null) {
|
||||
try {
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||
|
@ -123,7 +126,7 @@ public class ProofOfPossessionChallenge extends GenericChallenge {
|
|||
super.respond(cb);
|
||||
|
||||
try {
|
||||
cb.put("authorization", JsonUtil.parseJson(validation));
|
||||
cb.put(KEY_AUTHORIZATION, JsonUtil.parseJson(validation));
|
||||
} catch (JoseException ex) {
|
||||
// should not happen, as the JSON is prevalidated in the setter
|
||||
throw new IllegalStateException("validation: invalid JSON", ex);
|
||||
|
|
|
@ -17,16 +17,14 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.shredzone.acme4j.Account;
|
||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||
|
||||
/**
|
||||
* Implements the {@code tls-sni-01} challenge.
|
||||
*
|
||||
* @author Richard "Shred" Körber
|
||||
*/
|
||||
public class TlsSniChallenge extends GenericChallenge {
|
||||
public class TlsSniChallenge extends GenericTokenChallenge {
|
||||
private static final long serialVersionUID = 7370329525205430573L;
|
||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
||||
|
||||
|
@ -35,42 +33,21 @@ public class TlsSniChallenge extends GenericChallenge {
|
|||
*/
|
||||
public static final String TYPE = "tls-sni-01";
|
||||
|
||||
private String authorization = null;
|
||||
private String subject = null;
|
||||
|
||||
/**
|
||||
* Authorizes the {@link Challenge} by signing it with an {@link Account}.
|
||||
*
|
||||
* @param account
|
||||
* {@link Account} to sign the challenge with
|
||||
*/
|
||||
public void authorize(Account account) {
|
||||
if (account == null) {
|
||||
throw new NullPointerException("account must not be null");
|
||||
}
|
||||
|
||||
authorization = getToken() + '.' + Base64Url.encode(jwkThumbprint(account.getKeyPair().getPublic()));
|
||||
|
||||
String hash = computeHash(authorization);
|
||||
subject = hash.substring(0, 32) + '.' + hash.substring(32) + ".acme.invalid";
|
||||
}
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* Return the subject to generate a self-signed certificate for.
|
||||
*/
|
||||
public String getSubject() {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
assertIsAuthorized();
|
||||
return subject;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void respond(ClaimBuilder cb) {
|
||||
super.respond(cb);
|
||||
cb.put(KEY_TOKEN, getToken());
|
||||
cb.put(KEY_KEY_AUTHORIZATION, getAuthorization());
|
||||
public void authorize(Account account) {
|
||||
super.authorize(account);
|
||||
String hash = computeHash(getAuthorization());
|
||||
subject = hash.substring(0, 32) + '.' + hash.substring(32) + ".acme.invalid";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -103,16 +80,4 @@ public class TlsSniChallenge extends GenericChallenge {
|
|||
}
|
||||
}
|
||||
|
||||
private String getToken() {
|
||||
return get(KEY_TOKEN);
|
||||
}
|
||||
|
||||
private String getAuthorization() {
|
||||
if (authorization == null) {
|
||||
throw new IllegalStateException("Challenge is not authorized yet");
|
||||
}
|
||||
|
||||
return authorization;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue