revoke-cert sends JWK header

pull/55/head
Richard Körber 2017-05-03 10:59:30 +02:00
parent 0a63c65885
commit 7b6582ad78
8 changed files with 31 additions and 19 deletions

View File

@ -166,7 +166,7 @@ public class Certificate extends AcmeResource {
LOG.debug("revoke"); LOG.debug("revoke");
URL resUrl = getSession().resourceUrl(Resource.REVOKE_CERT); URL resUrl = getSession().resourceUrl(Resource.REVOKE_CERT);
if (resUrl == null) { if (resUrl == null) {
throw new AcmeProtocolException("CA does not support certificate revocation"); throw new AcmeException("Server does not allow certificate revocation");
} }
try (Connection conn = getSession().provider().connect()) { try (Connection conn = getSession().provider().connect()) {
@ -176,7 +176,7 @@ public class Certificate extends AcmeResource {
claims.put("reason", reason.getReasonCode()); claims.put("reason", reason.getReasonCode());
} }
conn.sendSignedRequest(resUrl, claims, getSession()); conn.sendSignedRequest(resUrl, claims, getSession(), true);
conn.accept(HttpURLConnection.HTTP_OK); conn.accept(HttpURLConnection.HTTP_OK);
} catch (CertificateEncodingException ex) { } catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex); throw new AcmeProtocolException("Invalid certificate", ex);

View File

@ -126,7 +126,7 @@ public class RegistrationBuilder {
createExternalAccountBinding(keyIdentifier, session.getKeyPair(), resourceUrl)); createExternalAccountBinding(keyIdentifier, session.getKeyPair(), resourceUrl));
} }
conn.sendJwkSignedRequest(resourceUrl, claims, session); conn.sendSignedRequest(resourceUrl, claims, session, true);
conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED); conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_CREATED);
URL location = conn.getLocation(); URL location = conn.getLocation();

View File

@ -49,8 +49,8 @@ public interface Connection extends AutoCloseable {
void sendRequest(URL url, Session session) throws AcmeException; void sendRequest(URL url, Session session) throws AcmeException;
/** /**
* Sends a signed POST request. Ensures that the session has a KeyIdentifier set that * Sends a signed POST request. Ensures that the session has a KeyIdentifier set, and
* is used in the "kid" protected header. * that the "kid" protected header field is used.
* *
* @param url * @param url
* {@link URL} to send the request to. * {@link URL} to send the request to.
@ -63,7 +63,7 @@ public interface Connection extends AutoCloseable {
/** /**
* Sends a signed POST request. If the session's KeyIdentifier is set, a "kid" * Sends a signed POST request. If the session's KeyIdentifier is set, a "kid"
* protected header is sent. If not, a "jwk" protected header is sent. * protected header field is sent. If not, a "jwk" protected header field is sent.
* *
* @param url * @param url
* {@link URL} to send the request to. * {@link URL} to send the request to.
@ -71,8 +71,13 @@ public interface Connection extends AutoCloseable {
* {@link JSONBuilder} containing claims. Must not be {@code null}. * {@link JSONBuilder} containing claims. Must not be {@code null}.
* @param session * @param session
* {@link Session} instance to be used for signing and tracking * {@link Session} instance to be used for signing and tracking
* @param enforceJwk
* {@code true} to enforce a "jwk" header field even if a KeyIdentifier is
* set, {@code false} to choose between "kid" and "jwk" header field
* automatically
*/ */
void sendJwkSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException; void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
throws AcmeException;
/** /**
* Checks if the HTTP response status is in the given list of acceptable HTTP states, * Checks if the HTTP response status is in the given list of acceptable HTTP states,

View File

@ -152,11 +152,12 @@ public class DefaultConnection implements Connection {
throw new IllegalStateException("session has no KeyIdentifier set"); throw new IllegalStateException("session has no KeyIdentifier set");
} }
sendJwkSignedRequest(url, claims, session); sendSignedRequest(url, claims, session, false);
} }
@Override @Override
public void sendJwkSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
throws AcmeException {
Objects.requireNonNull(url, "url"); Objects.requireNonNull(url, "url");
Objects.requireNonNull(claims, "claims"); Objects.requireNonNull(claims, "claims");
Objects.requireNonNull(session, "session"); Objects.requireNonNull(session, "session");
@ -182,7 +183,9 @@ public class DefaultConnection implements Connection {
jws.setPayload(claims.toString()); jws.setPayload(claims.toString());
jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce())); jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce()));
jws.getHeaders().setObjectHeaderValue("url", url); jws.getHeaders().setObjectHeaderValue("url", url);
if (session.getKeyIdentifier() != null) { if (enforceJwk || session.getKeyIdentifier() == null) {
jws.getHeaders().setJwkHeaderValue("jwk", jwk);
} else {
// TODO PEBBLE: cannot process "kid" yet, send "jwk" instead // TODO PEBBLE: cannot process "kid" yet, send "jwk" instead
// https://github.com/letsencrypt/pebble/issues/23 // https://github.com/letsencrypt/pebble/issues/23
if (Pebble.workaround()) { if (Pebble.workaround()) {
@ -190,8 +193,6 @@ public class DefaultConnection implements Connection {
} else { } else {
jws.getHeaders().setObjectHeaderValue("kid", session.getKeyIdentifier()); jws.getHeaders().setObjectHeaderValue("kid", session.getKeyIdentifier());
} }
} else {
jws.getHeaders().setJwkHeaderValue("jwk", jwk);
} }
jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk));

View File

@ -136,10 +136,12 @@ public class CertificateTest {
} }
@Override @Override
public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
assertThat(url, is(resourceUrl)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateRequest").toString())); assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateRequest").toString()));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(session.getKeyIdentifier(), is(nullValue()));
assertThat(enforceJwk, is(true));
certRequested = false; certRequested = false;
} }
@ -188,10 +190,11 @@ public class CertificateTest {
} }
@Override @Override
public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
assertThat(url, is(resourceUrl)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateWithReasonRequest").toString())); assertThat(claims.toString(), sameJSONAs(getJSON("revokeCertificateWithReasonRequest").toString()));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(enforceJwk, is(true));
certRequested = false; certRequested = false;
} }

View File

@ -57,10 +57,11 @@ public class RegistrationBuilderTest {
} }
@Override @Override
public void sendJwkSignedRequest(URL url, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(url, is(resourceUrl)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJSON("newRegistration").toString())); assertThat(claims.toString(), sameJSONAs(getJSON("newRegistration").toString()));
assertThat(enforceJwk, is(true));
isUpdate = false; isUpdate = false;
} }
@ -120,10 +121,11 @@ public class RegistrationBuilderTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendJwkSignedRequest(URL url, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk) {
try { try {
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(url, is(resourceUrl)); assertThat(url, is(resourceUrl));
assertThat(enforceJwk, is(true));
JSON binding = claims.toJSON() JSON binding = claims.toJSON()
.get("external-account-binding") .get("external-account-binding")

View File

@ -747,7 +747,7 @@ public class DefaultConnectionTest {
}) { }) {
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
cb.put("foo", 123).put("bar", "a-string"); cb.put("foo", 123).put("bar", "a-string");
conn.sendJwkSignedRequest(requestUrl, cb, session); conn.sendSignedRequest(requestUrl, cb, session, true);
} }
verify(mockUrlConnection).setRequestMethod("POST"); verify(mockUrlConnection).setRequestMethod("POST");
@ -812,7 +812,7 @@ public class DefaultConnectionTest {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
conn.sendJwkSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session); conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session, true);
} }
} }

View File

@ -46,7 +46,8 @@ public class DummyConnection implements Connection {
} }
@Override @Override
public void sendJwkSignedRequest(URL url, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session, boolean enforceJwk)
throws AcmeException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }