From 4c4cf5b5cfe88a92f709a8bef7292bd025ed4ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 6 Dec 2017 22:55:00 +0100 Subject: [PATCH] Connect to Pebble via https --- .../provider/pebble/PebbleAcmeProvider.java | 10 ++- .../provider/pebble/PebbleHttpConnector.java | 75 ++++++++++++++++++ .../acme4j/provider/pebble/pebble.truststore | Bin 0 -> 1090 bytes .../pebble/PebbleAcmeProviderTest.java | 10 +-- .../org/shredzone/acme4j/it/PebbleITBase.java | 2 +- acme4j-it/src/test/pebble/pebble-config.json | 2 + 6 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleHttpConnector.java create mode 100644 acme4j-client/src/main/resources/org/shredzone/acme4j/provider/pebble/pebble.truststore diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java index 634764cd..0f884b22 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProvider.java @@ -19,6 +19,7 @@ import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.provider.AbstractAcmeProvider; import org.shredzone.acme4j.provider.AcmeProvider; @@ -47,7 +48,7 @@ public class PebbleAcmeProvider extends AbstractAcmeProvider { try { String path = serverUri.getPath(); - URL baseUrl = new URL("http://localhost:14000/dir"); + URL baseUrl = new URL("https://localhost:14000/dir"); if (path != null && !path.isEmpty() && !"/".equals(path)) { baseUrl = parsePath(path); @@ -74,10 +75,15 @@ public class PebbleAcmeProvider extends AbstractAcmeProvider { if (m.group(2) != null) { port = Integer.parseInt(m.group(2)); } - return new URL("http", host, port, "/dir"); + return new URL("https", host, port, "/dir"); } else { throw new IllegalArgumentException("Invalid Pebble host/port: " + path); } } + @Override + protected HttpConnector createHttpConnector() { + return new PebbleHttpConnector(); + } + } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleHttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleHttpConnector.java new file mode 100644 index 00000000..320a097f --- /dev/null +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/pebble/PebbleHttpConnector.java @@ -0,0 +1,75 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 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.pebble; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.shredzone.acme4j.connector.HttpConnector; + +/** + * {@link HttpConnector} to be used for Pebble. Pebble uses a static, self signed SSL + * certificate. + */ +public class PebbleHttpConnector extends HttpConnector { + + private static SSLSocketFactory sslSocketFactory; + + @Override + public HttpURLConnection openConnection(URL url) throws IOException { + HttpURLConnection conn = super.openConnection(url); + if (conn instanceof HttpsURLConnection) { + ((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory()); + } + return conn; + } + + /** + * Lazily creates an {@link SSLSocketFactory} that exclusively accepts the Pebble + * certificate. + */ + protected synchronized SSLSocketFactory createSocketFactory() throws IOException { + if (sslSocketFactory == null) { + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + keystore.load(getClass().getResourceAsStream("/org/shredzone/acme4j/provider/pebble/pebble.truststore"), + "acme4j".toCharArray()); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keystore); + + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, tmf.getTrustManagers(), null); + + sslSocketFactory = ctx.getSocketFactory(); + } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException + | KeyManagementException ex) { + throw new IOException("Could not create truststore", ex); + } + } + return sslSocketFactory; + } + +} diff --git a/acme4j-client/src/main/resources/org/shredzone/acme4j/provider/pebble/pebble.truststore b/acme4j-client/src/main/resources/org/shredzone/acme4j/provider/pebble/pebble.truststore new file mode 100644 index 0000000000000000000000000000000000000000..749bc26eb5a9c1318db10d183b64b48e9e49dbe2 GIT binary patch literal 1090 zcmV-I1ikw(f&@MS0Ru3C1NR0ADuzgg_YDCD0ic2d=mdfTE*;4p#%+y)6MhDe6@ z4FLxRpn?O*0JLxeKAn0j+Q z#}>GwNNJZzTs4)f{JyP+9dbEp4_1L*%jBRNQ@|%mzq_TT@Oiu{e=f z+0x$C)SgN3#&IUz^z+=Rdhr$SyuS*Ww89rzS@b3k3m7F8Ki4-LhF++N|1z>1SwJy) zg3jmwM9Bn1^feadvWf~}kY05b{5)8nKz}o9&4za?#$@j>(TQA3k1>S<*C5YP#Q;5= zUZQoy$5#q^C`n!``~k;rgpCOItoe(PU?J1$Od*2=y<0&+vg zUlH@4X6gOiIusQC8#_0RADN&ruK_2lk`!A>c5JhfDNg&DUDXGTT(^7^hLT5RyPAqm5>iuZEU0 ze3uMf$!TFcfR$MeBu#!v;B4n6wsq$s#Pa^6&Dc|!deNS$l@CJadWkJm?`TpBZT&{$ znPi)FN=`8O+Ln>KP^E%*pO+OG5sKE8zc3dc2j3&|LPkm7HYKId=&0>NBN$ zUf}bmv8iuMbniNbZ(<*7@HciK0&=kxrzuxIE;i%n#ZT^aEx@r_6@?mgn6J$Uh66f` z)OqF?R>WuohMN2+QpKx;fEy|B4pO(nmM$UERJ8OHVB2f zE4I9!-sT$bx~dNA=rJ;Eui%vUo2wE6hFg!c>^#I=2KNv)#Px7+SK~*=O?0z=V!P82 zv^~B8U#fCn^(^i8%rzVQPx{a`Ts7^Dy0|HFN}=$&K^PHGyhP_s_nG(N0hKShZX#AP zRC-2|SdltP_*5?1P=c$M>$zlz+6nd&M&ERR1?Vh)NN#rq)Qw)TsJMD`iCm!*;HX@p z74!xgIE;85hKBI^3wKC-F$!)W<*A=BITclvorf_$>=(9LQNJ(cA?}uTTF?KPgz@BJ zrI1UJ@%c|&ao!IHpT+;o z!+e9_yn=0Fs06P19=bb>{6Lb9r+a|jIWv_77BX1!GI?XTZ_rL*4i$#+G%!9eAutIB z1uG5%0vZJX1QeP7Yq93rNP_MR$obCD>N&jyqA&y$!&`PWRl}ug$>ob-V3b_+%I`ey I0s{etp!XU1zW@LL literal 0 HcmV?d00001 diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java index 1298bdf4..ac30ca70 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/pebble/PebbleAcmeProviderTest.java @@ -51,15 +51,15 @@ public class PebbleAcmeProviderTest { PebbleAcmeProvider provider = new PebbleAcmeProvider(); assertThat(provider.resolve(new URI("acme://pebble")), - is(url("http://localhost:14000/dir"))); + is(url("https://localhost:14000/dir"))); assertThat(provider.resolve(new URI("acme://pebble/")), - is(url("http://localhost:14000/dir"))); + is(url("https://localhost:14000/dir"))); assertThat(provider.resolve(new URI("acme://pebble/pebble.example.com")), - is(url("http://pebble.example.com:14000/dir"))); + is(url("https://pebble.example.com:14000/dir"))); assertThat(provider.resolve(new URI("acme://pebble/pebble.example.com:12345")), - is(url("http://pebble.example.com:12345/dir"))); + is(url("https://pebble.example.com:12345/dir"))); assertThat(provider.resolve(new URI("acme://pebble/pebble.example.com:12345/")), - is(url("http://pebble.example.com:12345/dir"))); + is(url("https://pebble.example.com:12345/dir"))); try { provider.resolve(new URI("acme://pebble/bad.example.com:port")); diff --git a/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java b/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java index 13a6074b..fb615b76 100644 --- a/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java +++ b/acme4j-it/src/test/java/org/shredzone/acme4j/it/PebbleITBase.java @@ -76,7 +76,7 @@ public abstract class PebbleITBase { */ protected void assertIsPebbleUrl(URL url) { assertThat(url, not(nullValue())); - assertThat(url.getProtocol(), is("http")); + assertThat(url.getProtocol(), is("https")); assertThat(url.getHost(), is(pebbleHost)); assertThat(url.getPort(), is(pebblePort)); assertThat(url.getPath(), not(isEmptyOrNullString())); diff --git a/acme4j-it/src/test/pebble/pebble-config.json b/acme4j-it/src/test/pebble/pebble-config.json index 6f51b3d4..a2d7f7b6 100644 --- a/acme4j-it/src/test/pebble/pebble-config.json +++ b/acme4j-it/src/test/pebble/pebble-config.json @@ -1,6 +1,8 @@ { "pebble": { "listenAddress": "0.0.0.0:14000", + "certificate": "/go/src/github.com/letsencrypt/pebble/test/certs/localhost/cert.pem", + "privateKey": "/go/src/github.com/letsencrypt/pebble/test/certs/localhost/key.pem", "httpPort": 5002, "tlsPort": 5001 }