Make the challenge selectable

pull/17/merge
Richard Körber 2015-12-26 16:21:10 +01:00
parent 7b6af21cd1
commit 78ccec7d1d
1 changed files with 139 additions and 28 deletions

View File

@ -26,7 +26,10 @@ import java.util.Collection;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.DnsChallenge;
import org.shredzone.acme4j.challenge.HttpChallenge; import org.shredzone.acme4j.challenge.HttpChallenge;
import org.shredzone.acme4j.challenge.TlsSniChallenge;
import org.shredzone.acme4j.exception.AcmeConflictException; import org.shredzone.acme4j.exception.AcmeConflictException;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeUnauthorizedException; import org.shredzone.acme4j.exception.AcmeUnauthorizedException;
@ -119,35 +122,12 @@ public class ClientTest {
} }
LOG.info("New authorization for domain " + domain); LOG.info("New authorization for domain " + domain);
// Find a single http-01 challenge // Uncomment a challenge...
HttpChallenge challenge = auth.findChallenge(HttpChallenge.TYPE); Challenge challenge = httpChallenge(auth, account, domain);
// Challenge challenge = dnsChallenge(auth, account, domain);
// Challenge challenge = tlsSniChallenge(auth, account, domain);
if (challenge == null) { if (challenge == null) {
LOG.error("Found no " + HttpChallenge.TYPE + " challenge, don't know what to do...");
return;
}
// Authorize the challenge
challenge.authorize(account);
// Output the challenge, wait for acknowledge...
LOG.info("Please create a file in your web server's base directory.");
LOG.info("It must be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken());
LOG.info("File name: " + challenge.getToken());
LOG.info("Content: " + challenge.getAuthorization());
LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!");
LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder();
message.append("Please create a file in your web server's base directory.\n\n");
message.append("http://").append(domain).append("/.well-known/acme-challenge/").append(challenge.getToken()).append("\n\n");
message.append("Content:\n\n");
message.append(challenge.getAuthorization());
int option = JOptionPane.showConfirmDialog(null,
message.toString(),
"Prepare Challenge",
JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
LOG.error("User cancelled challenge");
return; return;
} }
@ -211,6 +191,137 @@ public class ClientTest {
// client.revokeCertificate(account, cert); // client.revokeCertificate(account, cert);
} }
/**
* Prepares HTTP challenge.
*/
public Challenge httpChallenge(Authorization auth, Account account, String domain) throws AcmeException {
// Find a single http-01 challenge
HttpChallenge challenge = auth.findChallenge(HttpChallenge.TYPE);
if (challenge == null) {
LOG.error("Found no " + HttpChallenge.TYPE + " challenge, don't know what to do...");
return null;
}
// Authorize the challenge
challenge.authorize(account);
// Output the challenge, wait for acknowledge...
LOG.info("Please create a file in your web server's base directory.");
LOG.info("It must be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken());
LOG.info("File name: " + challenge.getToken());
LOG.info("Content: " + challenge.getAuthorization());
LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!");
LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder();
message.append("Please create a file in your web server's base directory.\n\n");
message.append("http://").append(domain).append("/.well-known/acme-challenge/").append(challenge.getToken()).append("\n\n");
message.append("Content:\n\n");
message.append(challenge.getAuthorization());
int option = JOptionPane.showConfirmDialog(null,
message.toString(),
"Prepare Challenge",
JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
LOG.error("User cancelled challenge");
return null;
}
return challenge;
}
/**
* Prepares DNS challenge.
*/
public Challenge dnsChallenge(Authorization auth, Account account, String domain) throws AcmeException {
// Find a single dns-01 challenge
DnsChallenge challenge = auth.findChallenge(DnsChallenge.TYPE);
if (challenge == null) {
LOG.error("Found no " + DnsChallenge.TYPE + " challenge, don't know what to do...");
return null;
}
// Authorize the challenge
challenge.authorize(account);
// Output the challenge, wait for acknowledge...
LOG.info("Please create a TXT record:");
LOG.info("_acme-challenge." + domain + ". IN TXT " + challenge.getDigest());
LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder();
message.append("Please create a TXT record:\n\n");
message.append("_acme-challenge." + domain + ". IN TXT " + challenge.getDigest());
int option = JOptionPane.showConfirmDialog(null,
message.toString(),
"Prepare Challenge",
JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
LOG.error("User cancelled challenge");
return null;
}
return challenge;
}
/**
* Prepares TLS-SNI challenge.
*/
public Challenge tlsSniChallenge(Authorization auth, Account account, String domain) throws AcmeException {
// Find a single tls-sni-01 challenge
TlsSniChallenge challenge = auth.findChallenge(TlsSniChallenge.TYPE);
if (challenge == null) {
LOG.error("Found no " + TlsSniChallenge.TYPE + " challenge, don't know what to do...");
return null;
}
// Authorize the challenge
challenge.authorize(account);
// Get the Subject
String subject = challenge.getSubject();
// Create a keypair
KeyPair domainKeyPair;
try (FileWriter fw = new FileWriter("tlssni.key")) {
domainKeyPair = KeyPairUtils.createKeyPair(2048);
KeyPairUtils.writeKeyPair(domainKeyPair, fw);
} catch (IOException ex) {
LOG.error("Could not create keypair", ex);
return null;
}
// Create a certificate
try (FileWriter fw = new FileWriter("tlssni.crt")) {
X509Certificate cert = CertificateUtils.createTlsSniCertificate(domainKeyPair, subject);
CertificateUtils.writeX509Certificate(cert, fw);
} catch (IOException ex) {
LOG.error("Could not create certificate", ex);
return null;
}
// Output the challenge, wait for acknowledge...
LOG.info("Please configure your web server.");
LOG.info("It must return the certificate 'tlssni.crt' on a SNI request to:");
LOG.info(subject);
LOG.info("The matching keypair is available at 'tlssni.key'.");
LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder();
message.append("Please use 'tlssni.key' and 'tlssni.crt' cert for SNI requests to:\n\n");
message.append("https://").append(subject).append("\n\n");
int option = JOptionPane.showConfirmDialog(null,
message.toString(),
"Prepare Challenge",
JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
LOG.error("User cancelled challenge");
return null;
}
return challenge;
}
/** /**
* Presents the user a link to the Terms of Service, and asks for confirmation. * Presents the user a link to the Terms of Service, and asks for confirmation.
* *