mirror of https://github.com/shred/acme4j
Deprecate tls-sni challenges
parent
a01a9165a8
commit
1fd515912a
|
@ -19,7 +19,12 @@ import org.shredzone.acme4j.Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@value TYPE} challenge.
|
* 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 {
|
public class TlsSni01Challenge extends TokenChallenge {
|
||||||
private static final long serialVersionUID = 7370329525205430573L;
|
private static final long serialVersionUID = 7370329525205430573L;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,11 @@ import org.shredzone.acme4j.Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@value TYPE} challenge.
|
* 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 {
|
public class TlsSni02Challenge extends TokenChallenge {
|
||||||
private static final long serialVersionUID = 8921833167878544518L;
|
private static final long serialVersionUID = 8921833167878544518L;
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON;
|
||||||
* Implementing classes must implement at least {@link AcmeProvider#accepts(URI)}
|
* Implementing classes must implement at least {@link AcmeProvider#accepts(URI)}
|
||||||
* and {@link AbstractAcmeProvider#resolve(URI)}.
|
* and {@link AbstractAcmeProvider#resolve(URI)}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public abstract class AbstractAcmeProvider implements AcmeProvider {
|
public abstract class AbstractAcmeProvider implements AcmeProvider {
|
||||||
|
|
||||||
private static final Map<String, Function<Session, Challenge>> CHALLENGES = challengeMap();
|
private static final Map<String, Function<Session, Challenge>> CHALLENGES = challengeMap();
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.shredzone.acme4j.toolbox.JSONBuilder;
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link Authorization}.
|
* Unit tests for {@link Authorization}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class AuthorizationTest {
|
public class AuthorizationTest {
|
||||||
|
|
||||||
private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge
|
private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class TlsSni01ChallengeTest {
|
||||||
* Test that {@link TlsSni01Challenge} generates a correct authorization key.
|
* Test that {@link TlsSni01Challenge} generates a correct authorization key.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void testTlsSniChallenge() throws IOException {
|
public void testTlsSniChallenge() throws IOException {
|
||||||
TlsSni01Challenge challenge = new TlsSni01Challenge(session);
|
TlsSni01Challenge challenge = new TlsSni01Challenge(session);
|
||||||
challenge.unmarshall(getJsonAsObject("tlsSniChallenge"));
|
challenge.unmarshall(getJsonAsObject("tlsSniChallenge"));
|
||||||
|
|
|
@ -45,6 +45,7 @@ public class TlsSni02ChallengeTest {
|
||||||
* Test that {@link TlsSni02Challenge} generates a correct authorization key.
|
* Test that {@link TlsSni02Challenge} generates a correct authorization key.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void testTlsSni02Challenge() throws IOException {
|
public void testTlsSni02Challenge() throws IOException {
|
||||||
TlsSni02Challenge challenge = new TlsSni02Challenge(session);
|
TlsSni02Challenge challenge = new TlsSni02Challenge(session);
|
||||||
challenge.unmarshall(getJsonAsObject("tlsSni02Challenge"));
|
challenge.unmarshall(getJsonAsObject("tlsSni02Challenge"));
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.shredzone.acme4j.toolbox.TestUtils;
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link AbstractAcmeProvider}.
|
* Unit tests for {@link AbstractAcmeProvider}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class AbstractAcmeProviderTest {
|
public class AbstractAcmeProviderTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class LetsEncryptHttpConnectorTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Category(HttpURLConnection.class)
|
@Category(HttpURLConnection.class)
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void testCertificate() throws IOException, URISyntaxException {
|
public void testCertificate() throws IOException, URISyntaxException {
|
||||||
LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector();
|
LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector();
|
||||||
|
|
||||||
|
@ -68,6 +69,7 @@ public class LetsEncryptHttpConnectorTest {
|
||||||
* Test that the {@link SSLSocketFactory} can be instantiated and is cached.
|
* Test that the {@link SSLSocketFactory} can be instantiated and is cached.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void testCreateSocketFactory() throws IOException {
|
public void testCreateSocketFactory() throws IOException {
|
||||||
LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector();
|
LetsEncryptHttpConnector connector = new LetsEncryptHttpConnector();
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class ClientTest {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ClientTest.class);
|
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
|
* Generates a certificate for the given domains. Also takes care for the registration
|
||||||
|
@ -210,10 +210,6 @@ public class ClientTest {
|
||||||
case DNS:
|
case DNS:
|
||||||
challenge = dnsChallenge(auth, domain);
|
challenge = dnsChallenge(auth, domain);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TLSSNI:
|
|
||||||
challenge = tlsSniChallenge(auth, domain);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (challenge == null) {
|
if (challenge == null) {
|
||||||
|
@ -329,63 +325,6 @@ public class ClientTest {
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares a TLS-SNI challenge.
|
|
||||||
* <p>
|
|
||||||
* The verification of this challenge expects that the web server returns a special
|
|
||||||
* validation certificate.
|
|
||||||
* <p>
|
|
||||||
* 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
|
* Presents the instructions for preparing the challenge validation, and waits for
|
||||||
* dismissal. If the user cancelled the dialog, an exception is thrown.
|
* dismissal. If the user cancelled the dialog, an exception is thrown.
|
||||||
|
|
|
@ -39,7 +39,6 @@ import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
|
||||||
import org.bouncycastle.operator.OperatorCreationException;
|
import org.bouncycastle.operator.OperatorCreationException;
|
||||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||||
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class offering convenience methods for certificates.
|
* Utility class offering convenience methods for certificates.
|
||||||
|
@ -177,14 +176,17 @@ public final class CertificateUtils {
|
||||||
* @param subject
|
* @param subject
|
||||||
* Subject to create a certificate for
|
* Subject to create a certificate for
|
||||||
* @return Created certificate
|
* @return Created certificate
|
||||||
|
* @deprecated The tls-sni-01 challenge is deprecated
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static X509Certificate createTlsSniCertificate(KeyPair keypair, String subject) throws IOException {
|
public static X509Certificate createTlsSniCertificate(KeyPair keypair, String subject) throws IOException {
|
||||||
return createCertificate(keypair, subject);
|
return createCertificate(keypair, subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a self-signed {@link X509Certificate} that can be used for
|
* 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
|
* @param keypair
|
||||||
* A domain {@link KeyPair} to be used for the challenge
|
* A domain {@link KeyPair} to be used for the challenge
|
||||||
|
@ -193,7 +195,9 @@ public final class CertificateUtils {
|
||||||
* @param sanB
|
* @param sanB
|
||||||
* SAN-B to be used in the certificate
|
* SAN-B to be used in the certificate
|
||||||
* @return Created certificate
|
* @return Created certificate
|
||||||
|
* @deprecated The tls-sni-02 challenge is deprecated
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static X509Certificate createTlsSni02Certificate(KeyPair keypair, String sanA, String sanB)
|
public static X509Certificate createTlsSni02Certificate(KeyPair keypair, String sanA, String sanB)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return createCertificate(keypair, sanA, sanB);
|
return createCertificate(keypair, sanA, sanB);
|
||||||
|
|
|
@ -125,8 +125,8 @@ public class CertificateUtilsTest {
|
||||||
* Test if {@link CertificateUtils#createTlsSniCertificate(KeyPair, String)} creates a
|
* Test if {@link CertificateUtils#createTlsSniCertificate(KeyPair, String)} creates a
|
||||||
* good certificate.
|
* good certificate.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("deprecation") // test deprecated method
|
|
||||||
public void testCreateTlsSniCertificate() throws IOException, CertificateParsingException {
|
public void testCreateTlsSniCertificate() throws IOException, CertificateParsingException {
|
||||||
String subject = "30c452b9bd088cdbc2c4094947025d7c.7364ea602ac325a1b55ceaae024fbe29.acme.invalid";
|
String subject = "30c452b9bd088cdbc2c4094947025d7c.7364ea602ac325a1b55ceaae024fbe29.acme.invalid";
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ public class CertificateUtilsTest {
|
||||||
* Test if {@link CertificateUtils#createTlsSni02Certificate(KeyPair, String, String)}
|
* Test if {@link CertificateUtils#createTlsSni02Certificate(KeyPair, String, String)}
|
||||||
* creates a good certificate.
|
* creates a good certificate.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void testCreateTlsSni02Certificate() throws IOException, CertificateParsingException {
|
public void testCreateTlsSni02Certificate() throws IOException, CertificateParsingException {
|
||||||
String sanA = "1082909237a535173c8415a44539f84e.248317530d8d1a0c71de8fd23f1beae4.token.acme.invalid";
|
String sanA = "1082909237a535173c8415a44539f84e.248317530d8d1a0c71de8fd23f1beae4.token.acme.invalid";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# tls-sni-01 Challenge
|
# 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.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# tls-sni-02 Challenge
|
# 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.
|
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.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue