From 78cb7259d492455981b2fdf03b1b16d83e0e1c78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Thu, 7 Jul 2016 00:11:39 +0200 Subject: [PATCH] Use certificate if already available on request time --- .../org/shredzone/acme4j/Certificate.java | 2 + .../org/shredzone/acme4j/Registration.java | 11 +-- .../shredzone/acme4j/RegistrationTest.java | 70 ++++++++++++++++++- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java index 4f9cb2e8..ba7ea9b2 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -90,6 +90,8 @@ public class Certificate extends AcmeResource { conn.throwAcmeException(); } + // TODO: HTTP_ACCEPTED plus Retry-After header if not yet available + chainCertUri = conn.getLink("up"); cert = conn.readCertificate(); diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java index 6e02e14f..8acc48cc 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java @@ -18,6 +18,7 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyPair; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -176,12 +177,14 @@ public class Registration extends AcmeResource { conn.throwAcmeException(); } - // HTTP_ACCEPTED requires Retry-After header to be set + X509Certificate cert = null; + if (rc == HttpURLConnection.HTTP_CREATED) { + cert = conn.readCertificate(); + } - // Optionally returns the certificate. Currently it is just ignored. - // X509Certificate cert = conn.readCertificate(); + URI chainCertUri = conn.getLink("up"); - return new Certificate(getSession(), conn.getLocation()); + return new Certificate(getSession(), conn.getLocation(), chainCertUri, cert); } catch (IOException ex) { throw new AcmeNetworkException(ex); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java index 59f48fe5..b8ec95ca 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java @@ -23,6 +23,7 @@ import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyPair; +import java.security.cert.X509Certificate; import java.util.Map; import org.jose4j.jws.JsonWebSignature; @@ -49,6 +50,7 @@ public class RegistrationTest { private URI resourceUri = URI.create("http://example.com/acme/resource"); private URI locationUri = URI.create("http://example.com/acme/registration"); private URI agreementUri = URI.create("http://example.com/agreement.pdf"); + private URI chainUri = URI.create("http://example.com/acme/chain"); /** * Test getters. Make sure object cannot be modified. @@ -171,11 +173,19 @@ public class RegistrationTest { } /** - * Test that a certificate can be requested. + * Test that a certificate can be requested and is delivered synchronously. */ @Test - public void testRequestCertificate() throws AcmeException, IOException { + public void testRequestCertificateSync() throws AcmeException, IOException { + final X509Certificate originalCert = TestUtils.createCertificate(); + TestableConnectionProvider provider = new TestableConnectionProvider() { + @Override + public int sendRequest(URI uri) { + fail("Attempted to download the certificate. Should be downloaded already!"); + return HttpURLConnection.HTTP_OK; + } + @Override public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session) { assertThat(uri, is(resourceUri)); @@ -184,6 +194,61 @@ public class RegistrationTest { return HttpURLConnection.HTTP_CREATED; } + @Override + public X509Certificate readCertificate() { + return originalCert; + } + + @Override + public URI getLocation() { + return locationUri; + } + + @Override + public URI getLink(String relation) { + switch(relation) { + case "up": return chainUri; + default: return null; + } + } + }; + + provider.putTestResource(Resource.NEW_CERT, resourceUri); + + byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); + + Registration registration = new Registration(provider.createSession(), locationUri); + Certificate cert = registration.requestCertificate(csr); + + assertThat(cert.download(), is(originalCert)); + assertThat(cert.getLocation(), is(locationUri)); + assertThat(cert.getChainLocation(), is(chainUri)); + + provider.close(); + } + + /** + * Test that a certificate can be requested and is delivered asynchronously. + */ + @Test + public void testRequestCertificateAsync() throws AcmeException, IOException { + TestableConnectionProvider provider = new TestableConnectionProvider() { + @Override + public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session) { + assertThat(uri, is(resourceUri)); + assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest"))); + assertThat(session, is(notNullValue())); + return HttpURLConnection.HTTP_ACCEPTED; + } + + @Override + public URI getLink(String relation) { + switch(relation) { + case "up": return chainUri; + default: return null; + } + } + @Override public URI getLocation() { return locationUri; @@ -198,6 +263,7 @@ public class RegistrationTest { Certificate cert = registration.requestCertificate(csr); assertThat(cert.getLocation(), is(locationUri)); + assertThat(cert.getChainLocation(), is(chainUri)); provider.close(); }