mirror of https://github.com/shred/acme4j
Changes for latest draft-ietf-acme-tls-alpn
parent
1cffd3428d
commit
95614e73c5
|
@ -35,9 +35,9 @@ public class TlsAlpn01Challenge extends TokenChallenge {
|
||||||
public static final String TYPE = "tls-alpn-01";
|
public static final String TYPE = "tls-alpn-01";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OID of the {@code acmeValidation-v1} extension.
|
* OID of the {@code acmeValidation} extension.
|
||||||
*/
|
*/
|
||||||
public static final String ACME_VALIDATION_V1_OID = "1.3.6.1.5.5.7.1.30.1";
|
public static final String ACME_VALIDATION_OID = "1.3.6.1.5.5.7.1.31";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code acme-tls/1} protocol.
|
* {@code acme-tls/1} protocol.
|
||||||
|
@ -57,10 +57,10 @@ public class TlsAlpn01Challenge extends TokenChallenge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value that is to be used as {@code acmeValidation-v1} extension in
|
* Returns the value that is to be used as {@code acmeValidation} extension in
|
||||||
* the test certificate.
|
* the test certificate.
|
||||||
*/
|
*/
|
||||||
public byte[] getAcmeValidationV1() {
|
public byte[] getAcmeValidation() {
|
||||||
return sha256hash(getAuthorization());
|
return sha256hash(getAuthorization());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class TlsAlpn01ChallengeTest {
|
||||||
assertThat(challenge.getStatus(), is(Status.PENDING));
|
assertThat(challenge.getStatus(), is(Status.PENDING));
|
||||||
assertThat(challenge.getToken(), is(TOKEN));
|
assertThat(challenge.getToken(), is(TOKEN));
|
||||||
assertThat(challenge.getAuthorization(), is(KEY_AUTHORIZATION));
|
assertThat(challenge.getAuthorization(), is(KEY_AUTHORIZATION));
|
||||||
assertThat(challenge.getAcmeValidationV1(), is(AcmeUtils.sha256hash(KEY_AUTHORIZATION)));
|
assertThat(challenge.getAcmeValidation(), is(AcmeUtils.sha256hash(KEY_AUTHORIZATION)));
|
||||||
|
|
||||||
JSONBuilder response = new JSONBuilder();
|
JSONBuilder response = new JSONBuilder();
|
||||||
challenge.prepareResponse(response);
|
challenge.prepareResponse(response);
|
||||||
|
|
|
@ -106,7 +106,7 @@ public class OrderIT extends PebbleITBase {
|
||||||
KeyPair challengeKey = createKeyPair();
|
KeyPair challengeKey = createKeyPair();
|
||||||
|
|
||||||
X509Certificate cert = CertificateUtils.createTlsAlpn01Certificate(
|
X509Certificate cert = CertificateUtils.createTlsAlpn01Certificate(
|
||||||
challengeKey, auth.getDomain(), challenge.getAcmeValidationV1());
|
challengeKey, auth.getDomain(), challenge.getAcmeValidation());
|
||||||
|
|
||||||
client.dnsAddARecord(TEST_DOMAIN, getBammBammHostname());
|
client.dnsAddARecord(TEST_DOMAIN, getBammBammHostname());
|
||||||
client.tlsAlpnAddCertificate(auth.getDomain(), challengeKey.getPrivate(), cert);
|
client.tlsAlpnAddCertificate(auth.getDomain(), challengeKey.getPrivate(), cert);
|
||||||
|
|
|
@ -53,12 +53,12 @@ import org.shredzone.acme4j.challenge.TlsAlpn01Challenge;
|
||||||
public final class CertificateUtils {
|
public final class CertificateUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code acmeValidation-v1} object identifier.
|
* The {@code acmeValidation} object identifier.
|
||||||
*
|
*
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public static final ASN1ObjectIdentifier ACME_VALIDATION_V1 =
|
public static final ASN1ObjectIdentifier ACME_VALIDATION =
|
||||||
new ASN1ObjectIdentifier(TlsAlpn01Challenge.ACME_VALIDATION_V1_OID).intern();
|
new ASN1ObjectIdentifier(TlsAlpn01Challenge.ACME_VALIDATION_OID).intern();
|
||||||
|
|
||||||
private CertificateUtils() {
|
private CertificateUtils() {
|
||||||
// utility class without constructor
|
// utility class without constructor
|
||||||
|
@ -90,18 +90,18 @@ public final class CertificateUtils {
|
||||||
* A domain {@link KeyPair} to be used for the challenge
|
* A domain {@link KeyPair} to be used for the challenge
|
||||||
* @param subject
|
* @param subject
|
||||||
* The subject (domain name) that is to be validated
|
* The subject (domain name) that is to be validated
|
||||||
* @param acmeValidationV1
|
* @param acmeValidation
|
||||||
* The value that is returned by
|
* The value that is returned by
|
||||||
* {@link TlsAlpn01Challenge#getAcmeValidationV1()}
|
* {@link TlsAlpn01Challenge#getAcmeValidation()}
|
||||||
* @return Created certificate
|
* @return Created certificate
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public static X509Certificate createTlsAlpn01Certificate(KeyPair keypair, String subject, byte[] acmeValidationV1)
|
public static X509Certificate createTlsAlpn01Certificate(KeyPair keypair, String subject, byte[] acmeValidation)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
Objects.requireNonNull(keypair, "keypair");
|
Objects.requireNonNull(keypair, "keypair");
|
||||||
Objects.requireNonNull(subject, "subject");
|
Objects.requireNonNull(subject, "subject");
|
||||||
if (acmeValidationV1 == null || acmeValidationV1.length != 32) {
|
if (acmeValidation == null || acmeValidation.length != 32) {
|
||||||
throw new IllegalArgumentException("Bad acmeValidationV1 parameter");
|
throw new IllegalArgumentException("Bad acmeValidation parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
|
@ -121,7 +121,7 @@ public final class CertificateUtils {
|
||||||
gns[0] = new GeneralName(GeneralName.dNSName, subject);
|
gns[0] = new GeneralName(GeneralName.dNSName, subject);
|
||||||
certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(gns));
|
certBuilder.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(gns));
|
||||||
|
|
||||||
certBuilder.addExtension(ACME_VALIDATION_V1, true, new DEROctetString(acmeValidationV1));
|
certBuilder.addExtension(ACME_VALIDATION, true, new DEROctetString(acmeValidation));
|
||||||
|
|
||||||
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlg);
|
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlg);
|
||||||
|
|
||||||
|
|
|
@ -105,9 +105,9 @@ public class CertificateUtilsTest {
|
||||||
assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid"));
|
assertThat(cert.getSubjectX500Principal().getName(), is("CN=acme.invalid"));
|
||||||
assertThat(getSANs(cert), contains(subject));
|
assertThat(getSANs(cert), contains(subject));
|
||||||
|
|
||||||
assertThat(cert.getCriticalExtensionOIDs(), hasItem(TlsAlpn01Challenge.ACME_VALIDATION_V1_OID));
|
assertThat(cert.getCriticalExtensionOIDs(), hasItem(TlsAlpn01Challenge.ACME_VALIDATION_OID));
|
||||||
|
|
||||||
byte[] encodedExtensionValue = cert.getExtensionValue(TlsAlpn01Challenge.ACME_VALIDATION_V1_OID);
|
byte[] encodedExtensionValue = cert.getExtensionValue(TlsAlpn01Challenge.ACME_VALIDATION_OID);
|
||||||
assertThat(encodedExtensionValue, is(notNullValue()));
|
assertThat(encodedExtensionValue, is(notNullValue()));
|
||||||
|
|
||||||
try (ASN1InputStream asn = new ASN1InputStream(new ByteArrayInputStream(encodedExtensionValue))) {
|
try (ASN1InputStream asn = new ASN1InputStream(new ByteArrayInputStream(encodedExtensionValue))) {
|
||||||
|
|
|
@ -7,15 +7,15 @@ With the `tls-alpn-01` challenge, you prove to the CA that you are able to contr
|
||||||
This challenge is not part of the ACME specifications. It is specified [in a separate IETF document](https://tools.ietf.org/html/draft-ietf-acme-tls-alpn) and is still work in progress.
|
This challenge is not part of the ACME specifications. It is specified [in a separate IETF document](https://tools.ietf.org/html/draft-ietf-acme-tls-alpn) and is still work in progress.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`TlsAlpn01Challenge` provides a byte array called `acmeValidationV1`:
|
`TlsAlpn01Challenge` provides a byte array called `acmeValidation`:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
TlsAlpn01Challenge challenge = auth.findChallenge(TlsAlpn01Challenge.TYPE);
|
TlsAlpn01Challenge challenge = auth.findChallenge(TlsAlpn01Challenge.TYPE);
|
||||||
|
|
||||||
byte[] acmeValidationV1 = challenge.getAcmeValidationV1();
|
byte[] acmeValidation = challenge.getAcmeValidation();
|
||||||
```
|
```
|
||||||
|
|
||||||
You need to create a self-signed certificate with the domain to be validated set as the only _Subject Alternative Name_. The `acmeValidationV1` must be set as DER encoded `OCTET STRING` extension with the object id `1.3.6.1.5.5.7.1.30.1`. It is required to set this extension as critical.
|
You need to create a self-signed certificate with the domain to be validated set as the only _Subject Alternative Name_. The `acmeValidation` must be set as DER encoded `OCTET STRING` extension with the object id `1.3.6.1.5.5.7.1.31`. It is required to set this extension as critical.
|
||||||
|
|
||||||
After that, configure your web server so it will use this certificate on an incoming TLS request having the SNI `subject` and the ALPN protocol `acme-tls/1`.
|
After that, configure your web server so it will use this certificate on an incoming TLS request having the SNI `subject` and the ALPN protocol `acme-tls/1`.
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ String subject = auth.getDomain();
|
||||||
KeyPair certKeyPair = KeyPairUtils.createKeyPair(2048);
|
KeyPair certKeyPair = KeyPairUtils.createKeyPair(2048);
|
||||||
|
|
||||||
X509Certificate cert = CertificateUtils.
|
X509Certificate cert = CertificateUtils.
|
||||||
createTlsAlpn01Certificate(certKeyPair, subject, acmeValidationV1);
|
createTlsAlpn01Certificate(certKeyPair, subject, acmeValidation);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now use `cert` and `certKeyPair` to let your web server respond to TLS requests containing an ALPN extension with the value `acme-tls/1` and a SNI extension containing `subject`.
|
Now use `cert` and `certKeyPair` to let your web server respond to TLS requests containing an ALPN extension with the value `acme-tls/1` and a SNI extension containing `subject`.
|
||||||
|
|
Loading…
Reference in New Issue