add support for fetching certificate chain

pull/17/head
argy 2016-05-29 03:44:27 +03:00
parent 41e9e177be
commit d5b4ff37dc
4 changed files with 104 additions and 12 deletions

View File

@ -135,9 +135,22 @@ public interface AcmeClient {
* {@link Registration} to be used for conversation
* @param csr
* PKCS#10 Certificate Signing Request to be sent to the server
* @return {@link URI} the certificate can be downloaded from
* @return {@link CertificateURIs} the certificate and certificate chain can be downloaded from
*/
URI requestCertificate(Registration registration, byte[] csr) throws AcmeException;
CertificateURIs requestCertificate(Registration registration, byte[] csr) throws AcmeException;
/**
* Downloads chain for certificate.
*
* @param chainCertUri
* Certificate {@link URI}
* @return Downloaded {@link X509Certificate[]}
*
* @throws AcmeException
* if an {@link IOException} is thrown during certificate retrieval
* or the max recursion limit is exceeded
*/
X509Certificate[] downloadCertificateChain(URI chainCertUri) throws AcmeException;
/**
* Downloads a certificate.

View File

@ -0,0 +1,41 @@
package org.shredzone.acme4j;
import java.net.URI;
/**
* Represents the URIs returned by a certificate request
*
* @author cargy
*
*/
public class CertificateURIs {
private final URI certUri;
private final URI chainCertUri;
public CertificateURIs(URI certUri, URI chainCertUri) {
this.certUri = certUri;
this.chainCertUri = chainCertUri;
}
/**
* The URI from which the client may fetch the certificate
*
* @return
* {@link URI} the certificate can be downloaded from
*/
public URI getCertUri() {
return certUri;
}
/**
* The URI from which the client may fetch a chain of CA certificates
*
* @return
* {@link URI} the certificate chain can be downloaded from
*/
public URI getChainCertUri() {
return chainCertUri;
}
}

View File

@ -28,10 +28,7 @@ import java.util.Map;
import org.jose4j.jwk.PublicJsonWebKey;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.AcmeClient;
import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.Registration;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.*;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.Resource;
@ -54,6 +51,7 @@ import org.slf4j.LoggerFactory;
*/
public abstract class AbstractAcmeClient implements AcmeClient {
private static final Logger LOG = LoggerFactory.getLogger(AbstractAcmeClient.class);
private static final int MAX_CHAIN_LENGTH = 10;
private final Session session = new Session();
@ -410,7 +408,7 @@ public abstract class AbstractAcmeClient implements AcmeClient {
}
@Override
public URI requestCertificate(Registration registration, byte[] csr) throws AcmeException {
public CertificateURIs requestCertificate(Registration registration, byte[] csr) throws AcmeException {
if (registration == null) {
throw new NullPointerException("registration must not be null");
}
@ -433,12 +431,42 @@ public abstract class AbstractAcmeClient implements AcmeClient {
// Optionally returns the certificate. Currently it is just ignored.
// X509Certificate cert = conn.readCertificate();
return conn.getLocation();
return new CertificateURIs(conn.getLocation(), conn.getLink("up"));
} catch (IOException ex) {
throw new AcmeNetworkException(ex);
}
}
@Override
public X509Certificate[] downloadCertificateChain(URI chainCertUri) throws AcmeException {
if (chainCertUri == null) {
throw new NullPointerException("certChainUri must not be null");
}
LOG.debug("getCertificateChain");
List<X509Certificate> certChain = new ArrayList<>();
URI link = chainCertUri;
while (link != null && certChain.size() < MAX_CHAIN_LENGTH) {
try (Connection conn = createConnection()) {
int rc = conn.sendRequest(chainCertUri);
if (rc != HttpURLConnection.HTTP_OK) {
conn.throwAcmeException();
}
certChain.add(conn.readCertificate());
link = conn.getLink("up");
} catch (IOException ex) {
throw new AcmeNetworkException(ex);
}
}
if (link != null)
throw new AcmeException("Recursion limit reached (" + MAX_CHAIN_LENGTH + "). Didn't get " + link);
return certChain.toArray(new X509Certificate[certChain.size()]);
}
@Override
public X509Certificate downloadCertificate(URI certUri) throws AcmeException {

View File

@ -32,6 +32,7 @@ import org.jose4j.lang.JoseException;
import org.junit.Before;
import org.junit.Test;
import org.shredzone.acme4j.Authorization;
import org.shredzone.acme4j.CertificateURIs;
import org.shredzone.acme4j.Registration;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.challenge.Challenge;
@ -56,6 +57,7 @@ public class AbstractAcmeClientTest {
private URI resourceUri;
private URI locationUri;
private URI certChainUri;
private URI agreementUri;
private KeyPair accountKeyPair;
private Registration testRegistration;
@ -64,6 +66,7 @@ public class AbstractAcmeClientTest {
public void setup() throws IOException, URISyntaxException {
resourceUri = new URI("https://example.com/acme/some-resource");
locationUri = new URI("https://example.com/acme/some-location");
certChainUri = new URI("https://example.com/acme/some-url");
agreementUri = new URI("http://example.com/agreement.pdf");
accountKeyPair = TestUtils.createKeyPair();
testRegistration = new Registration(accountKeyPair);
@ -456,7 +459,8 @@ public class AbstractAcmeClientTest {
@Test
public void testRequestCertificate() throws AcmeException, IOException {
Connection connection = new DummyConnection() {
@Override
@Override
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session, Registration registration) {
assertThat(uri, is(resourceUri));
assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest")));
@ -469,15 +473,21 @@ public class AbstractAcmeClientTest {
public URI getLocation() {
return locationUri;
}
@Override
public URI getLink(String relation) {
return certChainUri;
}
};
TestableAbstractAcmeClient client = new TestableAbstractAcmeClient(connection);
client.putTestResource(Resource.NEW_CERT, resourceUri);
byte[] csr = TestUtils.getResourceAsByteArray("/csr.der");
URI certUri = client.requestCertificate(testRegistration, csr);
CertificateURIs certUris = client.requestCertificate(testRegistration, csr);
assertThat(certUri, is(locationUri));
assertThat(certUris.getCertUri(), is(locationUri));
assertThat(certUris.getChainCertUri(), is(certChainUri));
}
/**