Probably done with ACME support

Let's Encrypt
This commit is contained in:
Norbi Peti 2017-06-01 21:19:11 +02:00
parent e05c7230a6
commit 9ed0a5580b
4 changed files with 60 additions and 47 deletions

24
pom.xml
View file

@ -42,16 +42,24 @@
<goal>shade</goal> <goal>shade</goal>
</goals> </goals>
<configuration> <configuration>
<artifactSet> <!-- <artifactSet> <includes> <include>org.shredzone.acme4j:acme4j-client</include>
<includes> <include>org.shredzone.acme4j:acme4j-utils</include> <include>org.bouncycastle:bcprov-jdk15on</include>
<include>org.shredzone.acme4j:acme4j-client</include> </includes> </artifactSet> -->
</includes>
</artifactSet>
<pluginExecution> <pluginExecution>
<action> <action>
<execute /> <execute />
</action> </action>
</pluginExecution> </pluginExecution>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
@ -82,35 +90,41 @@
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.9.2-R0.1-SNAPSHOT</version> <version>1.9.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.4</version> <version>3.4</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.reflections/reflections --> <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>
<version>0.9.10</version> <version>0.9.10</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.javassist/javassist --> <!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency> <dependency>
<groupId>org.javassist</groupId> <groupId>org.javassist</groupId>
<artifactId>javassist</artifactId> <artifactId>javassist</artifactId>
<version>3.20.0-GA</version> <version>3.20.0-GA</version>
<scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io --> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>1.3.2</version> <version>1.3.2</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId> <groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>ButtonCore</artifactId> <artifactId>ButtonCore</artifactId>
<version>master-SNAPSHOT</version> <version>master-SNAPSHOT</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.shredzone.acme4j</groupId> <groupId>org.shredzone.acme4j</groupId>

View file

@ -13,10 +13,12 @@
*/ //Modified */ //Modified
package buttondevteam.website; package buttondevteam.website;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Writer; import java.io.Writer;
import java.net.URI; import java.net.URI;
import java.security.KeyPair; import java.security.KeyPair;
@ -25,8 +27,6 @@ import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import javax.swing.JOptionPane;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.shredzone.acme4j.*; import org.shredzone.acme4j.*;
import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Challenge;
@ -40,6 +40,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.website.page.AcmeChallengePage;
/** /**
* A simple client test tool. * A simple client test tool.
@ -151,7 +152,7 @@ public class AcmeClient {
* {@link Session} to bind with * {@link Session} to bind with
* @return {@link Registration} connected to your account * @return {@link Registration} connected to your account
*/ */
private Registration findOrRegisterAccount(Session session) throws AcmeException { private Registration findOrRegisterAccount(Session session) throws AcmeException, IOException {
Registration reg; Registration reg;
try { try {
@ -245,45 +246,31 @@ public class AcmeClient {
* Domain name to be authorized * Domain name to be authorized
* @return {@link Challenge} to verify * @return {@link Challenge} to verify
*/ */
@SuppressWarnings("unused")
public Challenge httpChallenge(Authorization auth, String domain) throws AcmeException { public Challenge httpChallenge(Authorization auth, String domain) throws AcmeException {
// Find a single http-01 challenge // Find a single http-01 challenge
Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE); Http01Challenge challenge = auth.findChallenge(Http01Challenge.TYPE);
if (challenge == null) { if (challenge == null) {
throw new AcmeException("Found no " + Http01Challenge.TYPE + " challenge, don't know what to do..."); throw new AcmeException("Found no " + Http01Challenge.TYPE + " challenge, don't know what to do...");
} }
if (ButtonWebsiteModule.PORT == 80)
// Output the challenge, wait for acknowledge... LOG.info("Storing the challenge data.");
LOG.info("Please create a file in your web server's base directory."); else
LOG.info("It must be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken()); LOG.info("Store the challenge data! Can't do automatically.");
LOG.info("It should be reachable at: http://" + domain + "/.well-known/acme-challenge/" + challenge.getToken());
LOG.info("File name: " + challenge.getToken()); LOG.info("File name: " + challenge.getToken());
LOG.info("Content: " + challenge.getAuthorization()); LOG.info("Content: " + challenge.getAuthorization());
LOG.info("The file must not contain any leading or trailing whitespaces or line breaks!"); LOG.info("Press any key to continue...");
LOG.info("If you're ready, dismiss the dialog..."); if (ButtonWebsiteModule.PORT != 80)
try {
StringBuilder message = new StringBuilder(); System.in.read();
message.append("Please create a file in your web server's base directory.\n\n"); } catch (IOException e) {
message.append("http://").append(domain).append("/.well-known/acme-challenge/").append(challenge.getToken()) e.printStackTrace();
.append("\n\n"); }
message.append("Content:\n\n"); ButtonWebsiteModule.addPage(new AcmeChallengePage(challenge.getToken(), challenge.getAuthorization()));
message.append(challenge.getAuthorization());
acceptChallenge(message.toString());
return challenge; return challenge;
} }
/**
* Presents the instructions for preparing the challenge validation, and waits for dismissal. If the user cancelled the dialog, an exception is thrown.
*
* @param message
* Instructions to be shown in the dialog
*/
public void acceptChallenge(String message) throws AcmeException {
int option = JOptionPane.showConfirmDialog(null, message, "Prepare Challenge", JOptionPane.OK_CANCEL_OPTION);
if (option == JOptionPane.CANCEL_OPTION) {
throw new AcmeException("User cancelled the challenge");
}
}
/** /**
* Presents the user a link to the Terms of Service, and asks for confirmation. If the user denies confirmation, an exception is thrown. * Presents the user a link to the Terms of Service, and asks for confirmation. If the user denies confirmation, an exception is thrown.
* *
@ -292,10 +279,10 @@ public class AcmeClient {
* @param agreement * @param agreement
* {@link URI} of the Terms of Service * {@link URI} of the Terms of Service
*/ */
public void acceptAgreement(Registration reg, URI agreement) throws AcmeException { public void acceptAgreement(Registration reg, URI agreement) throws AcmeException, IOException {
int option = JOptionPane.showConfirmDialog(null, "Do you accept the Terms of Service?\n\n" + agreement, BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
"Accept ToS", JOptionPane.YES_NO_OPTION); System.out.println("Do you accept the terms? (y/n)");
if (option == JOptionPane.NO_OPTION) { if (br.readLine().equalsIgnoreCase("y\n")) {
throw new AcmeException("User did not accept Terms of Service"); throw new AcmeException("User did not accept Terms of Service");
} }
@ -312,8 +299,8 @@ public class AcmeClient {
*/ */
public static void main(String... args) { public static void main(String... args) {
if (args.length == 0) { if (args.length == 0) {
System.err.println("Usage: ClientTest <domain>..."); TBMCCoreAPI.SendException("Error while doing ACME!", new Exception("No domains given"));
System.exit(1); return;
} }
LOG.info("Starting up..."); LOG.info("Starting up...");

View file

@ -11,11 +11,12 @@ import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.website.page.*; import buttondevteam.website.page.*;
public class ButtonWebsiteModule extends JavaPlugin { public class ButtonWebsiteModule extends JavaPlugin {
public static final int PORT = 8080;
private static HttpServer server; private static HttpServer server;
public ButtonWebsiteModule() { public ButtonWebsiteModule() {
try { try {
server = HttpServer.create(new InetSocketAddress((InetAddress) null, 8080), 10); server = HttpServer.create(new InetSocketAddress((InetAddress) null, PORT), 10);
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while starting the webserver!", e); TBMCCoreAPI.SendException("An error occured while starting the webserver!", e);
} }
@ -25,10 +26,12 @@ public class ButtonWebsiteModule extends JavaPlugin {
public void onEnable() { public void onEnable() {
addPage(new IndexPage()); addPage(new IndexPage());
Bukkit.getScheduler().runTaskAsynchronously(this, () -> { Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
this.getLogger().info("Starting webserver..."); this.getLogger().info("Starting webserver...");
((Runnable) server::start).run(); // Totally normal way of calling a method ((Runnable) server::start).run(); // Totally normal way of calling a method
this.getLogger().info("Webserver started"); this.getLogger().info("Webserver started");
Thread t = new Thread(() -> AcmeClient.main("server.figytuna.com"));
t.setContextClassLoader(getClass().getClassLoader());
t.start();
}); });
} }

View file

@ -6,15 +6,24 @@ import buttondevteam.website.io.Response;
public class AcmeChallengePage extends Page { public class AcmeChallengePage extends Page {
public AcmeChallengePage(String token, String content) {
this.token = token;
this.content = content;
}
@Override @Override
public String GetName() { public String GetName() {
return ".well-known/acme-challenge"; return ".well-known/acme-challenge/" + token;
} }
@Override @Override
public Response handlePage(HttpExchange exchange) { public Response handlePage(HttpExchange exchange) {
// TODO Auto-generated method stub if (content == null)
return null; return new Response(500, "500 No content", exchange);
return new Response(200, content, exchange);
} }
private String token;
private String content;
} }