mirror of https://github.com/shred/acme4j
Make the challenge selectable
parent
7b6af21cd1
commit
78ccec7d1d
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue