Remove deprecated code

acmev1
Richard Körber 2018-03-17 13:30:34 +01:00
parent f44b62d3cf
commit 0465d1941a
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
17 changed files with 10 additions and 679 deletions

View File

@ -1,52 +0,0 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 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 java.net.URL;
import org.shredzone.acme4j.Session;
/**
* Implements the {@value TYPE} challenge.
*
* @deprecated This challenge has been removed from the ACMEv2 specs.
*/
@Deprecated
public class OutOfBand01Challenge extends Challenge {
private static final long serialVersionUID = -7459595198486630582L;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "oob-01";
/**
* Creates a new generic {@link OutOfBand01Challenge} object.
*
* @param session
* {@link Session} to bind to.
*/
public OutOfBand01Challenge(Session session) {
super(session);
}
/**
* Returns the validation URL to be visited by the customer in order to complete the
* challenge.
*/
public URL getValidationUrl() {
return getJSON().get("href").asURL();
}
}

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.toolbox.AcmeUtils.*;
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;
/**
* 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

@ -1,80 +0,0 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 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.toolbox.AcmeUtils.*;
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;
/**
* Challenge type name: {@value}
*/
public static final String TYPE = "tls-sni-02";
private String subject;
private String sanB;
/**
* Creates a new generic {@link TlsSni02Challenge} object.
*
* @param session
* {@link Session} to bind to.
*/
public TlsSni02Challenge(Session session) {
super(session);
}
/**
* Returns the subject, which is to be used as "SAN-A" in a self-signed certificate.
* The CA will send the SNI request against this domain.
*/
public String getSubject() {
return subject;
}
/**
* Returns the key authorization, which is to be used as "SAN-B" in a self-signed
* certificate.
*/
public String getSanB() {
return sanB;
}
@Override
protected boolean acceptable(String type) {
return TYPE.equals(type);
}
@Override
protected void authorize() {
super.authorize();
String tokenHash = hexEncode(sha256hash(getToken()));
subject = tokenHash.substring(0, 32) + '.' + tokenHash.substring(32) + ".token.acme.invalid";
String kaHash = hexEncode(sha256hash(getAuthorization()));
sanB = kaHash.substring(0, 32) + '.' + kaHash.substring(32) + ".ka.acme.invalid";
}
}

View File

@ -25,9 +25,6 @@ import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.challenge.OutOfBand01Challenge;
import org.shredzone.acme4j.challenge.TlsSni01Challenge;
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector;
@ -41,7 +38,6 @@ 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<String, Function<Session, Challenge>> CHALLENGES = challengeMap();
@ -68,10 +64,7 @@ public abstract class AbstractAcmeProvider implements AcmeProvider {
Map<String, Function<Session, Challenge>> map = new HashMap<>();
map.put(Dns01Challenge.TYPE, Dns01Challenge::new);
map.put(TlsSni01Challenge.TYPE, TlsSni01Challenge::new);
map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new);
map.put(Http01Challenge.TYPE, Http01Challenge::new);
map.put(OutOfBand01Challenge.TYPE, OutOfBand01Challenge::new);
return Collections.unmodifiableMap(map);
}

View File

@ -30,7 +30,6 @@ import org.junit.Test;
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.AcmeException;
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
import org.shredzone.acme4j.provider.TestableConnectionProvider;
@ -40,7 +39,6 @@ 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
@ -63,10 +61,6 @@ public class AuthorizationTest {
Challenge c2 = authorization.findChallenge(Http01Challenge.TYPE);
assertThat(c2, is(notNullValue()));
assertThat(c2, is(instanceOf(Http01Challenge.class)));
// TlsSniChallenge is available, but not as standalone challenge
Challenge c3 = authorization.findChallenge(TlsSni02Challenge.TYPE);
assertThat(c3, is(nullValue()));
}
/**
@ -84,31 +78,19 @@ public class AuthorizationTest {
assertThat(c1, contains(instanceOf(Http01Challenge.class)));
// Available combined challenge
Collection<Challenge> c2 = authorization.findCombination(Dns01Challenge.TYPE, TlsSni02Challenge.TYPE);
assertThat(c2, hasSize(2));
assertThat(c2, contains(instanceOf(Dns01Challenge.class),
instanceOf(TlsSni02Challenge.class)));
Collection<Challenge> c2 = authorization.findCombination(Dns01Challenge.TYPE, Http01Challenge.TYPE);
assertThat(c2, hasSize(1));
assertThat(c2, contains(instanceOf(Http01Challenge.class)));
// Order does not matter
Collection<Challenge> c3 = authorization.findCombination(TlsSni02Challenge.TYPE, Dns01Challenge.TYPE);
assertThat(c3, hasSize(2));
assertThat(c3, contains(instanceOf(Dns01Challenge.class),
instanceOf(TlsSni02Challenge.class)));
Collection<Challenge> c3 = authorization.findCombination(Http01Challenge.TYPE, Dns01Challenge.TYPE);
assertThat(c3, hasSize(1));
assertThat(c3, contains(instanceOf(Http01Challenge.class)));
// Finds smaller combinations as well
Collection<Challenge> c4 = authorization.findCombination(Dns01Challenge.TYPE, TlsSni02Challenge.TYPE, SNAILMAIL_TYPE);
assertThat(c4, hasSize(2));
assertThat(c4, contains(instanceOf(Dns01Challenge.class),
instanceOf(TlsSni02Challenge.class)));
// Finds the smallest possible combination
Collection<Challenge> c5 = authorization.findCombination(Dns01Challenge.TYPE, TlsSni02Challenge.TYPE, Http01Challenge.TYPE);
assertThat(c5, hasSize(1));
assertThat(c5, contains(instanceOf(Http01Challenge.class)));
// Finds only entire combinations
Collection<Challenge> c6 = authorization.findCombination(Dns01Challenge.TYPE);
assertThat(c6, is(empty()));
Collection<Challenge> c4 = authorization.findCombination(Dns01Challenge.TYPE, Http01Challenge.TYPE, SNAILMAIL_TYPE);
assertThat(c4, hasSize(1));
assertThat(c4, contains(instanceOf(Http01Challenge.class)));
// Does not find challenges that have not been provided
Collection<Challenge> c7 = authorization.findCombination(SNAILMAIL_TYPE);
@ -327,7 +309,6 @@ public class AuthorizationTest {
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session));
provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session));
provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session));
Authorization authorization = new Authorization(session, locationUrl);
authorization.unmarshalAuthorization(getJsonAsObject("authorizationChallenges"));

View File

@ -1,62 +0,0 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 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.toolbox.TestUtils.getJsonAsObject;
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException;
import java.net.URL;
import org.junit.BeforeClass;
import org.junit.Test;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link OutOfBand01Challenge}.
*/
public class OutOfBandChallengeTest {
private static Session session;
@BeforeClass
public static void setup() throws IOException {
session = TestUtils.session();
}
/**
* Test that {@link OutOfBand01Challenge} is returning the validation URL.
*/
@Test
@SuppressWarnings("deprecation")
public void testHttpChallenge() throws IOException {
OutOfBand01Challenge challenge = new OutOfBand01Challenge(session);
challenge.unmarshall(getJsonAsObject("oobChallenge"));
assertThat(challenge.getType(), is(OutOfBand01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));
assertThat(challenge.getValidationUrl(),
is(new URL("https://example.com/validate/evaGxfADs6pSRb2LAv9IZ")));
JSONBuilder cb = new JSONBuilder();
challenge.respond(cb);
assertThat(cb.toString(), sameJSONAs("{\"type\": \"oob-01\"}"));
}
}

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.toolbox.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.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link TlsSni01Challenge}.
*/
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
@SuppressWarnings("deprecation")
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

@ -1,65 +0,0 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2016 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.toolbox.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.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link TlsSni02Challenge}.
*/
public class TlsSni02ChallengeTest {
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 TlsSni02Challenge} generates a correct authorization key.
*/
@Test
@SuppressWarnings("deprecation")
public void testTlsSni02Challenge() throws IOException {
TlsSni02Challenge challenge = new TlsSni02Challenge(session);
challenge.unmarshall(getJsonAsObject("tlsSni02Challenge"));
assertThat(challenge.getType(), is(TlsSni02Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.PENDING));
assertThat(challenge.getSubject(), is("5bf0b9908ed73bc53ed3327afa52f76b.0a4bea00520f0753f42abe0bb39e3ea8.token.acme.invalid"));
assertThat(challenge.getSanB(), is("14e2350a04434f93c2e0b6012968d99d.ed459b6a7a019d9695609b8514f9d63d.ka.acme.invalid"));
JSONBuilder cb = new JSONBuilder();
challenge.respond(cb);
assertThat(cb.toString(), sameJSONAs("{\"keyAuthorization\"=\""
+ KEY_AUTHORIZATION + "\"}").allowingExtraUnexpectedFields());
}
}

View File

@ -30,8 +30,6 @@ import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.challenge.OutOfBand01Challenge;
import org.shredzone.acme4j.challenge.TlsSni02Challenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector;
@ -41,7 +39,6 @@ import org.shredzone.acme4j.toolbox.TestUtils;
/**
* Unit tests for {@link AbstractAcmeProvider}.
*/
@SuppressWarnings("deprecation")
public class AbstractAcmeProviderTest {
/**
@ -148,21 +145,9 @@ 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));
Challenge c6 = provider.createChallenge(session, "foobar-01");
assertThat(c6, is(nullValue()));
Challenge c7 = provider.createChallenge(session, OutOfBand01Challenge.TYPE);
assertThat(c7, not(nullValue()));
assertThat(c7, instanceOf(OutOfBand01Challenge.class));
Challenge c8 = provider.createChallenge(session, "");
assertThat(c8, is(nullValue()));

View File

@ -201,14 +201,9 @@ authorizationChallenges = \
"type": "dns-01",\
"uri": "https://example.com/authz/asdf/1",\
"token": "DGyRejmCefe7v4NfDGDKfA"\
},\
{\
"type": "tls-sni-02",\
"uri": "https://example.com/authz/asdf/2",\
"token": "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ"\
}\
],\
"combinations": [[0], [1,2]]\
"combinations": [[0],[1]]\
}
@ -245,24 +240,4 @@ httpNoTokenChallenge = \
"status":"pending" \
}
tlsSniChallenge = \
{ \
"type":"tls-sni-01", \
"status":"pending", \
"token": "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ" \
}
tlsSni02Challenge = \
{ \
"type":"tls-sni-02", \
"status":"pending", \
"token": "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ" \
}
oobChallenge = \
{ \
"type": "oob-01", \
"href": "https://example.com/validate/evaGxfADs6pSRb2LAv9IZ" \
}
#

View File

@ -132,22 +132,6 @@ public final class CertificateUtils {
}
}
/**
* Writes an X.509 certificate chain PEM file.
*
* @param chain
* {@link X509Certificate[]} to write
* @param w
* {@link Writer} to write the PEM file to. The {@link Writer} is closed
* after use.
* @deprecated Use
* {@link #writeX509CertificateChain(Writer, X509Certificate, X509Certificate...)}
*/
@Deprecated
public static void writeX509CertificateChain(X509Certificate[] chain, Writer w) throws IOException {
writeX509CertificateChain(w, null, chain);
}
/**
* Reads a CSR PEM file.
*
@ -166,43 +150,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 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 org.shredzone.acme4j.challenge.TlsSni02Challenge}. The certificate is valid
* for 7 days.
*
* @param keypair
* A domain {@link KeyPair} to be used for the challenge
* @param sanA
* SAN-A to be used in the certificate
* @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);
}
/**
* Creates a generic self-signed challenge {@link X509Certificate}. The certificate is
* valid for 7 days.

View File

@ -28,9 +28,6 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -121,55 +118,6 @@ public class CertificateUtilsTest {
assertThat(countCertificates(out), is(3));
}
/**
* Test if {@link CertificateUtils#createTlsSniCertificate(KeyPair, String)} creates a
* good certificate.
*/
@SuppressWarnings("deprecation")
@Test
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.
*/
@SuppressWarnings("deprecation")
@Test
public void testCreateTlsSni02Certificate() throws IOException, CertificateParsingException {
String sanA = "1082909237a535173c8415a44539f84e.248317530d8d1a0c71de8fd23f1beae4.token.acme.invalid";
String sanB = "edc3a1d40199c1723358d57853bc23ff.4d4473417a6d76e80df17bbcfbe53d2c.ka.acme.invalid";
KeyPair keypair = KeyPairUtils.createKeyPair(2048);
X509Certificate cert = CertificateUtils.createTlsSni02Certificate(keypair, sanA, sanB);
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(sanA, sanB));
}
/**
* Test if {@link CertificateUtils#readCSR(InputStream)} reads an identical CSR.
*/

View File

@ -10,6 +10,3 @@ 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,11 +0,0 @@
# oob-01 Challenge
> **NOTE:** `oob-01` challenge has been removed in ACMEv2.
The `oob-01` challenge is an "out of band" challenge that is used when there is no automatic way of validating ownership of a domain. The client is instead required to perform actions outside of the ACME protocol.
`OutOfBand01Challenge` implements this challenge. Its `getValidationUrl()` method returns a URL that refers to a web page with further instructions about the actions to be taken.
The challenge must be triggered before the URL is opened in a browser.
Due to the nature of this challenge, it may take a considerable amount of time until its state changes to `VALID`.

View File

@ -1,44 +0,0 @@
# tls-sni-01 Challenge
> **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.
`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,46 +0,0 @@
# tls-sni-02 Challenge
> **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.
`TlsSni02Challenge` provides a subject and a key-authorization domain:
```java
TlsSni02Challenge challenge = auth.findChallenge(TlsSni02Challenge.TYPE);
String subject = challenge.getSubject(); // SAN-A
String sanB = challenge.getSanB(); // SAN-B
```
`subject` and `sanB` are basically domain names formed like in this example:
```
5bf0b9908ed73bc53ed3327afa52f76b.0a4bea00520f0753f42abe0bb39e3ea8.token.acme.invalid
14e2350a04434f93c2e0b6012968d99d.ed459b6a7a019d9695609b8514f9d63d.ka.acme.invalid
```
You need to create a self-signed certificate with both `subject` and `sanB` set as _Subject Alternative Name_. After that, configure your web server so it will use this certificate on a SNI request to `subject`.
The `TlsSni02Challenge` 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.createTlsSni02Certificate(sniKeyPair, subject, sanB);
```
Now use `cert` and `sniKeyPair` to let your web server respond to SNI requests to `subject`. The CA is not allowed to reveal `sanB`, so it will not perform SNI requests to that domain.
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 both `subject` and `sanB` set as `X509v3 Subject Alternative Name`.

View File

@ -40,9 +40,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>
<item name="CAs" href="ca/index.html">
<item name="Let's Encrypt" href="ca/letsencrypt.html"/>