From 1fd515912aca466035a8cb1a06d0aade8517388c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 13 Jan 2018 14:02:59 +0100 Subject: [PATCH] Deprecate tls-sni challenges --- .../acme4j/challenge/TlsSni01Challenge.java | 5 ++ .../acme4j/challenge/TlsSni02Challenge.java | 4 ++ .../acme4j/provider/AbstractAcmeProvider.java | 1 + .../shredzone/acme4j/AuthorizationTest.java | 1 + .../challenge/TlsSni01ChallengeTest.java | 1 + .../challenge/TlsSni02ChallengeTest.java | 1 + .../provider/AbstractAcmeProviderTest.java | 1 + .../LetsEncryptHttpConnectorTest.java | 2 + .../java/org/shredzone/acme4j/ClientTest.java | 63 +------------------ .../acme4j/util/CertificateUtils.java | 8 ++- .../acme4j/util/CertificateUtilsTest.java | 3 +- src/site/markdown/challenge/tls-sni-01.md | 2 +- src/site/markdown/challenge/tls-sni-02.md | 2 +- 13 files changed, 27 insertions(+), 67 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java index c4397c09..a4c18a47 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java @@ -19,7 +19,12 @@ import org.shredzone.acme4j.Session; /** * Implements the {@value TYPE} challenge. + * + * @deprecated This challenge is vulnerable and will be removed from the ACME specs. Do + * not use! Let's Encrypt does not offer this challenge to the general public + * any more. */ +@Deprecated public class TlsSni01Challenge extends TokenChallenge { private static final long serialVersionUID = 7370329525205430573L; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java index 03404655..cd8d21c9 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java @@ -19,7 +19,11 @@ import org.shredzone.acme4j.Session; /** * Implements the {@value TYPE} challenge. + * + * @deprecated This challenge is vulnerable and will be removed from the ACME specs. Do + * not use! */ +@Deprecated public class TlsSni02Challenge extends TokenChallenge { private static final long serialVersionUID = 8921833167878544518L; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java index d649cd28..79ba2a8e 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java @@ -41,6 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON; * Implementing classes must implement at least {@link AcmeProvider#accepts(URI)} * and {@link AbstractAcmeProvider#resolve(URI)}. */ +@SuppressWarnings("deprecation") public abstract class AbstractAcmeProvider implements AcmeProvider { private static final Map> CHALLENGES = challengeMap(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index 9dcbcdfa..8bfee1e7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -40,6 +40,7 @@ import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Unit tests for {@link Authorization}. */ +@SuppressWarnings("deprecation") public class AuthorizationTest { private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java index dbc40a83..69929609 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java @@ -45,6 +45,7 @@ public class TlsSni01ChallengeTest { * Test that {@link TlsSni01Challenge} generates a correct authorization key. */ @Test + @SuppressWarnings("deprecation") public void testTlsSniChallenge() throws IOException { TlsSni01Challenge challenge = new TlsSni01Challenge(session); challenge.unmarshall(getJsonAsObject("tlsSniChallenge")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java index 759a495c..46b388e4 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java @@ -45,6 +45,7 @@ public class TlsSni02ChallengeTest { * Test that {@link TlsSni02Challenge} generates a correct authorization key. */ @Test + @SuppressWarnings("deprecation") public void testTlsSni02Challenge() throws IOException { TlsSni02Challenge challenge = new TlsSni02Challenge(session); challenge.unmarshall(getJsonAsObject("tlsSni02Challenge")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java index 0a22ab45..2540a3ab 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java @@ -41,6 +41,7 @@ import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link AbstractAcmeProvider}. */ +@SuppressWarnings("deprecation") public class AbstractAcmeProviderTest { /** diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java index 2adbebaf..64143deb 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java @@ -41,6 +41,7 @@ public class LetsEncryptHttpConnectorTest { */ @Test @Category(HttpURLConnection.class) + @SuppressWarnings("deprecation") public void testCertificate() throws IOException, URISyntaxException { LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector(); @@ -68,6 +69,7 @@ public class LetsEncryptHttpConnectorTest { * Test that the {@link SSLSocketFactory} can be instantiated and is cached. */ @Test + @SuppressWarnings("deprecation") public void testCreateSocketFactory() throws IOException { LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector(); diff --git a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java index 6bd896f0..f41cf55b 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -65,7 +65,7 @@ public class ClientTest { private static final Logger LOG = LoggerFactory.getLogger(ClientTest.class); - private enum ChallengeType { HTTP, DNS, TLSSNI } + private enum ChallengeType { HTTP, DNS } /** * Generates a certificate for the given domains. Also takes care for the registration @@ -210,10 +210,6 @@ public class ClientTest { case DNS: challenge = dnsChallenge(auth, domain); break; - - case TLSSNI: - challenge = tlsSniChallenge(auth, domain); - break; } if (challenge == null) { @@ -329,63 +325,6 @@ public class ClientTest { return challenge; } - /** - * Prepares a TLS-SNI challenge. - *

- * The verification of this challenge expects that the web server returns a special - * validation certificate. - *

- * This example outputs instructions that need to be executed manually. In a - * production environment, you would rather configure your web server automatically. - * - * @param auth - * {@link Authorization} to find the challenge in - * @param domain - * Domain name to be authorized - * @return {@link Challenge} to verify - */ - public Challenge tlsSniChallenge(Authorization auth, String domain) throws AcmeException { - // Find a single tls-sni-01 challenge - org.shredzone.acme4j.challenge.TlsSni01Challenge challenge = auth.findChallenge(org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE); - if (challenge == null) { - throw new AcmeException("Found no " + org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE + " challenge, don't know what to do..."); - } - - // Get the Subject - String subject = challenge.getSubject(); - - // Create a validation key pair - KeyPair domainKeyPair; - try (FileWriter fw = new FileWriter("tlssni.key")) { - domainKeyPair = KeyPairUtils.createKeyPair(2048); - KeyPairUtils.writeKeyPair(domainKeyPair, fw); - } catch (IOException ex) { - throw new AcmeException("Could not write keypair", ex); - } - - // Create a validation certificate - try (FileWriter fw = new FileWriter("tlssni.crt")) { - X509Certificate cert = CertificateUtils.createTlsSniCertificate(domainKeyPair, subject); - CertificateUtils.writeX509Certificate(cert, fw); - } catch (IOException ex) { - throw new AcmeException("Could not write certificate", ex); - } - - // 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"); - acceptChallenge(message.toString()); - - return challenge; - } - /** * Presents the instructions for preparing the challenge validation, and waits for * dismissal. If the user cancelled the dialog, an exception is thrown. diff --git a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java index 276e5e25..422ae809 100644 --- a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java +++ b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java @@ -39,7 +39,6 @@ import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.shredzone.acme4j.challenge.TlsSni02Challenge; /** * Utility class offering convenience methods for certificates. @@ -177,14 +176,17 @@ public final class CertificateUtils { * @param subject * Subject to create a certificate for * @return Created certificate + * @deprecated The tls-sni-01 challenge is deprecated */ + @Deprecated public static X509Certificate createTlsSniCertificate(KeyPair keypair, String subject) throws IOException { return createCertificate(keypair, subject); } /** * Creates a self-signed {@link X509Certificate} that can be used for - * {@link TlsSni02Challenge}. The certificate is valid for 7 days. + * {@link org.shredzone.acme4j.challenge.TlsSni02Challenge}. The certificate is valid + * for 7 days. * * @param keypair * A domain {@link KeyPair} to be used for the challenge @@ -193,7 +195,9 @@ public final class CertificateUtils { * @param sanB * SAN-B to be used in the certificate * @return Created certificate + * @deprecated The tls-sni-02 challenge is deprecated */ + @Deprecated public static X509Certificate createTlsSni02Certificate(KeyPair keypair, String sanA, String sanB) throws IOException { return createCertificate(keypair, sanA, sanB); diff --git a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java index fb41e761..2791ff64 100644 --- a/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java +++ b/acme4j-utils/src/test/java/org/shredzone/acme4j/util/CertificateUtilsTest.java @@ -125,8 +125,8 @@ public class CertificateUtilsTest { * Test if {@link CertificateUtils#createTlsSniCertificate(KeyPair, String)} creates a * good certificate. */ + @SuppressWarnings("deprecation") @Test - @SuppressWarnings("deprecation") // test deprecated method public void testCreateTlsSniCertificate() throws IOException, CertificateParsingException { String subject = "30c452b9bd088cdbc2c4094947025d7c.7364ea602ac325a1b55ceaae024fbe29.acme.invalid"; @@ -149,6 +149,7 @@ public class CertificateUtilsTest { * Test if {@link CertificateUtils#createTlsSni02Certificate(KeyPair, String, String)} * creates a good certificate. */ + @SuppressWarnings("deprecation") @Test public void testCreateTlsSni02Certificate() throws IOException, CertificateParsingException { String sanA = "1082909237a535173c8415a44539f84e.248317530d8d1a0c71de8fd23f1beae4.token.acme.invalid"; diff --git a/src/site/markdown/challenge/tls-sni-01.md b/src/site/markdown/challenge/tls-sni-01.md index 510666cb..58afcdb2 100644 --- a/src/site/markdown/challenge/tls-sni-01.md +++ b/src/site/markdown/challenge/tls-sni-01.md @@ -1,6 +1,6 @@ # tls-sni-01 Challenge -> **NOTE:** In ACMEv2, this challenge is going to be replaced by [tls-sni-02](./tls-sni-02.html). However, the _Let's Encrypt_ ACMEv1 server is still offering this challenge as the only TLS-SNI based challenge. To be on the safe side, request both challenges and process the one that is returned. +> **SECURITY:** [This challenge is vulnerable in shared hosting environments](https://community.letsencrypt.org/t/2018-01-09-issue-with-tls-sni-01-and-shared-hosting-infrastructure/49996), and is going to be removed from the ACME specs. _Let's Encrypt_ does not offer this challenge to the general public any more. With the `tls-sni-01` challenge, you prove to the CA that you are able to control the web server of the domain to be authorized, by letting it respond to a SNI request with a specific self-signed cert. diff --git a/src/site/markdown/challenge/tls-sni-02.md b/src/site/markdown/challenge/tls-sni-02.md index 457537b5..cb121364 100644 --- a/src/site/markdown/challenge/tls-sni-02.md +++ b/src/site/markdown/challenge/tls-sni-02.md @@ -1,6 +1,6 @@ # tls-sni-02 Challenge -> **NOTE:** According to the ACME specifications, this challenge will replace [tls-sni-01](./tls-sni-01.html). However, _Let's Encrypt_ does not currently support `tls-sni-02`. To be on the safe side, request both challenges and process the one that is returned. +> **SECURITY:** [This challenge is vulnerable in shared hosting environments](https://community.letsencrypt.org/t/2018-01-09-issue-with-tls-sni-01-and-shared-hosting-infrastructure/49996), and is going to be removed from the ACME specs. With the `tls-sni-02` challenge, you prove to the CA that you are able to control the web server of the domain to be authorized, by letting it respond to a SNI request with a specific self-signed cert.