Remove deprecated tls-sni-01 challenge

pull/55/head
Richard Körber 2017-02-11 16:23:34 +01:00
parent 4312331fd9
commit 76ccb4587c
12 changed files with 6 additions and 241 deletions

View File

@ -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";
}
}

View File

@ -62,12 +62,10 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
}
}
@SuppressWarnings("deprecation") // must still provide deprecated challenges
private static Map<String, Function<Session, Challenge>> challengeMap() {
Map<String, Function<Session, Challenge>> 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);

View File

@ -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());
}
}

View File

@ -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));

View File

@ -234,13 +234,6 @@ httpNoTokenChallenge = \
"status":"pending" \
}
tlsSniChallenge = \
{ \
"type":"tls-sni-01", \
"status":"pending", \
"token": "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ" \
}
tlsSni02Challenge = \
{ \
"type":"tls-sni-02", \

View File

@ -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);

View File

@ -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.

View File

@ -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.

View File

@ -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)

View File

@ -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`.

View File

@ -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:

View File

@ -39,7 +39,6 @@
<item name="Challenges" href="challenge/index.html">
<item name="http-01" href="challenge/http-01.html"/>
<item name="dns-01" href="challenge/dns-01.html"/>
<item name="tls-sni-01" href="challenge/tls-sni-01.html"/>
<item name="tls-sni-02" href="challenge/tls-sni-02.html"/>
<item name="oob-01" href="challenge/oob-01.html"/>
</item>