From 57e9b593d286ecae72ac0db8ff481b006d24919d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 10 Jul 2017 23:13:01 +0200 Subject: [PATCH] Add a way to revoke a certificate without account key pair --- .../org/shredzone/acme4j/Certificate.java | 19 ++++++++++ .../org/shredzone/acme4j/CertificateTest.java | 35 +++++++++++++++++++ src/site/markdown/usage/certificate.md | 17 +++++++++ 3 files changed, 71 insertions(+) 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 ea4b7510..028404fc 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -183,4 +183,23 @@ public class Certificate extends AcmeResource { } } + /** + * Revoke a certificate. This call is meant to be used for revoking certificates if + * the account's key pair was lost. + * + * @param session + * {@link Session} to be used. Here you can also generate a session by + * using the key pair that was used for signing the CSR. + * @param cert + * {@link X509Certificate} to be revoked + * @param reason + * {@link RevocationReason} stating the reason of the revocation that is + * used when generating OCSP responses and CRLs. {@code null} to give no + * reason. + */ + public static void revoke(Session session, X509Certificate cert, + RevocationReason reason) throws AcmeException { + new Certificate(session, URI.create(""), null, cert).revoke(reason); + } + } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java index 49e4b4a7..f5b7ee40 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java @@ -21,6 +21,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; @@ -202,6 +203,40 @@ public class CertificateTest { provider.close(); } + /** + * Test that a certificate can be revoked by its domain key pair. + */ + @Test + public void testRevokeCertificateByKeyPair() throws AcmeException, IOException { + final X509Certificate originalCert = TestUtils.createCertificate(); + final KeyPair certKeyPair = TestUtils.createDomainKeyPair(); + + TestableConnectionProvider provider = new TestableConnectionProvider() { + @Override + public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + assertThat(uri, is(resourceUri)); + assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); + assertThat(session, is(notNullValue())); + assertThat(session.getKeyPair(), is(certKeyPair)); + } + + @Override + public int accept(int... httpStatus) throws AcmeException { + assertThat(httpStatus, isIntArrayContainingInAnyOrder(HttpURLConnection.HTTP_OK)); + return HttpURLConnection.HTTP_OK; + } + }; + + provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + + Session session = provider.createSession(); + session.setKeyPair(certKeyPair); + + Certificate.revoke(session, originalCert, RevocationReason.KEY_COMPROMISE); + + provider.close(); + } + /** * Test that numeric revocation reasons are correctly translated. */ diff --git a/src/site/markdown/usage/certificate.md b/src/site/markdown/usage/certificate.md index 1ca17fe0..7fff510a 100644 --- a/src/site/markdown/usage/certificate.md +++ b/src/site/markdown/usage/certificate.md @@ -135,3 +135,20 @@ Optionally, you can provide a revocation reason that the ACME server may use whe ```java cert.revoke(RevocationReason.KEY_COMPROMISE); ``` + +## Revocation without account key pair + +If you have lost your account key, you can still revoke a certificate as long as you still own the key pair that was used for signing the CSR. `Certificate` provides a special method for this case. + +First, create a new `Session` object, but use _the key pair that was used for siging the CSR_. Now invoke the `revoke()` method and pass the `Session`, the certificate to be revoked, and (optionally) a revocation reason. + +```java +KeyPair domainKeyPair = ... // the key pair that was used for signing the CSR +URI acmeServerUri = ... // uri of the ACME server +X509Certificate cert = ... // certificate to revoke + +Session session = new Session(acmeServerUri, domainKeyPair); +Certificate.revoke(session, cert, RevocationReason.KEY_COMPROMISE); +``` + +Note that there is no way to revoke a certificate if you should lose both your account's key pair and your domain's key pair.