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 deleted file mode 100644 index 6ab04f5f..00000000 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * acme4j - Java ACME client - * - * Copyright (C) 2015 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.challenge; - -import static org.shredzone.acme4j.util.AcmeUtils.*; - -import org.shredzone.acme4j.Session; - -/** - * Implements the {@value TYPE} challenge. - * - * @deprecated Use {@link TlsSni02Challenge} if supported by the CA. This challenge will - * be removed when Let's Encrypt removes support for - * {@link TlsSni01Challenge}. - */ -@Deprecated -public class TlsSni01Challenge extends TokenChallenge { - private static final long serialVersionUID = 7370329525205430573L; - - /** - * Challenge type name: {@value} - */ - public static final String TYPE = "tls-sni-01"; - - private String subject; - - /** - * Creates a new generic {@link TlsSni01Challenge} object. - * - * @param session - * {@link Session} to bind to. - */ - public TlsSni01Challenge(Session session) { - super(session); - } - - /** - * Return the subject to generate a self-signed certificate for. - */ - public String getSubject() { - return subject; - } - - @Override - protected boolean acceptable(String type) { - return TYPE.equals(type); - } - - @Override - protected void authorize() { - super.authorize(); - - String hash = hexEncode(sha256hash(getAuthorization())); - subject = hash.substring(0, 32) + '.' + hash.substring(32) + ".acme.invalid"; - } - -} 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 1d1d224e..4df84ec8 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 @@ -62,12 +62,10 @@ public abstract class AbstractAcmeProvider implements AcmeProvider { } } - @SuppressWarnings("deprecation") // must still provide deprecated challenges private static Map> challengeMap() { Map> map = new HashMap<>(); map.put(Dns01Challenge.TYPE, Dns01Challenge::new); - map.put(org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE, org.shredzone.acme4j.challenge.TlsSni01Challenge::new); map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new); map.put(Http01Challenge.TYPE, Http01Challenge::new); map.put(OutOfBand01Challenge.TYPE, OutOfBand01Challenge::new); 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 deleted file mode 100644 index ace8014d..00000000 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * acme4j - Java ACME client - * - * Copyright (C) 2015 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.challenge; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; -import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; - -import java.io.IOException; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.shredzone.acme4j.Session; -import org.shredzone.acme4j.Status; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; - -/** - * Unit tests for {@link TlsSni01Challenge}. - */ -@SuppressWarnings("deprecation") // must test a deprecated challenge -public class TlsSni01ChallengeTest { - private static final String KEY_AUTHORIZATION = - "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0"; - - private static Session session; - - @BeforeClass - public static void setup() throws IOException { - session = TestUtils.session(); - } - - /** - * Test that {@link TlsSni01Challenge} generates a correct authorization key. - */ - @Test - public void testTlsSniChallenge() throws IOException { - TlsSni01Challenge challenge = new TlsSni01Challenge(session); - challenge.unmarshall(getJsonAsObject("tlsSniChallenge")); - - assertThat(challenge.getType(), is(TlsSni01Challenge.TYPE)); - assertThat(challenge.getStatus(), is(Status.PENDING)); - assertThat(challenge.getSubject(), is("14e2350a04434f93c2e0b6012968d99d.ed459b6a7a019d9695609b8514f9d63d.acme.invalid")); - - JSONBuilder cb = new JSONBuilder(); - challenge.respond(cb); - - assertThat(cb.toString(), sameJSONAs("{\"keyAuthorization\"=\"" - + KEY_AUTHORIZATION + "\"}").allowingExtraUnexpectedFields()); - } - -} 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 83be73a3..b46b9d30 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 @@ -120,7 +120,6 @@ public class AbstractAcmeProviderTest { * Test that challenges are generated properly. */ @Test - @SuppressWarnings("deprecation") // must test deprecated challenges public void testCreateChallenge() { Session session = mock(Session.class); @@ -147,10 +146,6 @@ public class AbstractAcmeProviderTest { assertThat(c3, not(nullValue())); assertThat(c3, instanceOf(Dns01Challenge.class)); - Challenge c4 = provider.createChallenge(session, org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE); - assertThat(c4, not(nullValue())); - assertThat(c4, instanceOf(org.shredzone.acme4j.challenge.TlsSni01Challenge.class)); - Challenge c5 = provider.createChallenge(session, TlsSni02Challenge.TYPE); assertThat(c5, not(nullValue())); assertThat(c5, instanceOf(TlsSni02Challenge.class)); diff --git a/acme4j-client/src/test/resources/json.properties b/acme4j-client/src/test/resources/json.properties index c46e727d..79fa75fa 100644 --- a/acme4j-client/src/test/resources/json.properties +++ b/acme4j-client/src/test/resources/json.properties @@ -234,13 +234,6 @@ httpNoTokenChallenge = \ "status":"pending" \ } -tlsSniChallenge = \ - { \ - "type":"tls-sni-01", \ - "status":"pending", \ - "token": "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ" \ - } - tlsSni02Challenge = \ { \ "type":"tls-sni-02", \ 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 0b46dfd3..2978f194 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -31,6 +31,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Dns01Challenge; import org.shredzone.acme4j.challenge.Http01Challenge; +import org.shredzone.acme4j.challenge.TlsSni02Challenge; import org.shredzone.acme4j.exception.AcmeConflictException; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.util.CSRBuilder; @@ -368,16 +369,16 @@ public class ClientTest { * Domain name to be authorized * @return {@link Challenge} to verify */ - @SuppressWarnings("deprecation") // until tls-sni-02 is supported 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); + // Find a single tls-sni-02 challenge + TlsSni02Challenge challenge = auth.findChallenge(TlsSni02Challenge.TYPE); if (challenge == null) { - throw new AcmeException("Found no " + org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE + " challenge, don't know what to do..."); + throw new AcmeException("Found no " + TlsSni02Challenge.TYPE + " challenge, don't know what to do..."); } // Get the Subject String subject = challenge.getSubject(); + String sanB = challenge.getSanB(); // Create a validation key pair KeyPair domainKeyPair; @@ -390,7 +391,7 @@ public class ClientTest { // Create a validation certificate try (FileWriter fw = new FileWriter("tlssni.crt")) { - X509Certificate cert = CertificateUtils.createTlsSniCertificate(domainKeyPair, subject); + X509Certificate cert = CertificateUtils.createTlsSni02Certificate(domainKeyPair, subject, sanB); CertificateUtils.writeX509Certificate(cert, fw); } catch (IOException ex) { throw new AcmeException("Could not write certificate", ex); 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 c0169a3a..a5c3c9eb 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 @@ -167,24 +167,6 @@ public final class CertificateUtils { } } - /** - * Creates a self-signed {@link X509Certificate} that can be used for - * {@link org.shredzone.acme4j.challenge.TlsSni01Challenge}. The certificate is valid - * for 7 days. - * - * @param keypair - * A domain {@link KeyPair} to be used for the challenge - * @param subject - * Subject to create a certificate for - * @return Created certificate - * @deprecated Will be removed when - * {@link org.shredzone.acme4j.challenge.TlsSni01Challenge} is removed - */ - @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. 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..fecdeb21 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 @@ -121,30 +121,6 @@ public class CertificateUtilsTest { assertThat(countCertificates(out), is(3)); } - /** - * Test if {@link CertificateUtils#createTlsSniCertificate(KeyPair, String)} creates a - * good certificate. - */ - @Test - @SuppressWarnings("deprecation") // test deprecated method - public void testCreateTlsSniCertificate() throws IOException, CertificateParsingException { - String subject = "30c452b9bd088cdbc2c4094947025d7c.7364ea602ac325a1b55ceaae024fbe29.acme.invalid"; - - KeyPair keypair = KeyPairUtils.createKeyPair(2048); - - X509Certificate cert = CertificateUtils.createTlsSniCertificate(keypair, subject); - - Instant now = Instant.now(); - Instant end = now.plus(Duration.ofDays(8)); - - assertThat(cert, not(nullValue())); - assertThat(cert.getNotAfter(), is(greaterThan(Date.from(now)))); - assertThat(cert.getNotAfter(), is(lessThan(Date.from(end)))); - assertThat(cert.getNotBefore(), is(lessThanOrEqualTo(Date.from(now)))); - assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid")); - assertThat(getSANs(cert), containsInAnyOrder(subject)); - } - /** * Test if {@link CertificateUtils#createTlsSni02Certificate(KeyPair, String, String)} * creates a good certificate. diff --git a/src/site/markdown/challenge/index.md b/src/site/markdown/challenge/index.md index cba4c31d..5d919e21 100644 --- a/src/site/markdown/challenge/index.md +++ b/src/site/markdown/challenge/index.md @@ -10,6 +10,5 @@ The ACME specifications define these standard challenges: * [http-01](./http-01.html) * [dns-01](./dns-01.html) -* [tls-sni-01](./tls-sni-01.html) * [tls-sni-02](./tls-sni-02.html) * [oob-01](./oob-01.html) diff --git a/src/site/markdown/challenge/tls-sni-01.md b/src/site/markdown/challenge/tls-sni-01.md deleted file mode 100644 index 0b8b2c33..00000000 --- a/src/site/markdown/challenge/tls-sni-01.md +++ /dev/null @@ -1,44 +0,0 @@ -# tls-sni-01 Challenge - -> **DEPRECATED:** According to the ACME specifications, this challenge will be replaced by [tls-sni-02](./tls-sni-02.html). However, _Let's Encrypt_ does not currently support `tls-sni-02`. For the time being, _acme4j_ supports both challenges. To be on the safe side, request both challenges and process the one that is returned. - -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. - -`TlsSni01Challenge` provides a subject: - -```java -TlsSni01Challenge challenge = auth.findChallenge(TlsSni01Challenge.TYPE); - -String subject = challenge.getSubject(); -``` - -The `subject` is basically a domain name formed like in this example: - -``` -30c452b9bd088cdbc2c4094947025d7c.7364ea602ac325a1b55ceaae024fbe29.acme.invalid -``` - -You need to create a self-signed certificate with the subject set as _Subject Alternative Name_. After that, configure your web server so it will use this certificate on a SNI request to the `subject`. - -The `TlsSni01Challenge` class does not generate a self-signed certificate, as it would require _Bouncy Castle_. However, there is a utility method in the _acme4j-utils_ module for this use case: - -```java -KeyPair sniKeyPair = KeyPairUtils.createKeyPair(2048); -X509Certificate cert = CertificateUtils.createTlsSniCertificate(sniKeyPair, subject); -``` - -Now use `cert` and `sniKeyPair` to let your web server respond to a SNI request to `subject`. - -The challenge is completed when the CA was able to send the SNI request and get the correct certificate in return. - -Note that the request is sent to port 443 only. There is no way to choose a different port, for security reasons. This is a limitation of the ACME protocol, not of _acme4j_. - -This shell command line may be helpful to test your web server configuration: - -```shell -echo QUIT | \ - openssl s_client -servername $subject -connect $server_ip:443 | \ - openssl x509 -text -noout -``` - -It should return a certificate with `subject` set as `X509v3 Subject Alternative Name`. diff --git a/src/site/markdown/challenge/tls-sni-02.md b/src/site/markdown/challenge/tls-sni-02.md index f7979954..77c02a34 100644 --- a/src/site/markdown/challenge/tls-sni-02.md +++ b/src/site/markdown/challenge/tls-sni-02.md @@ -1,7 +1,5 @@ # 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`. For the time being, _acme4j_ supports both challenges. To be on the safe side, request both challenges and process the one that is returned. - 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. `TlsSni02Challenge` provides a subject and a key-authorization domain: diff --git a/src/site/site.xml b/src/site/site.xml index 4664b3c8..a364133d 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -39,7 +39,6 @@ -