From db4bf52674859479f744126e2e1a7d47382249e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 19 Apr 2017 00:50:08 +0200 Subject: [PATCH 01/38] Exception message wording --- .../src/main/java/org/shredzone/acme4j/AcmeResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java index 4e01fae8..ee2d7fd1 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java @@ -41,7 +41,7 @@ public abstract class AcmeResource implements Serializable { */ protected Session getSession() { if (session == null) { - throw new IllegalStateException("Use Acme.reconnect() for reconnecting this object to a session."); + throw new IllegalStateException("Use rebind() to bind this resource to a session."); } return session; From 0fe7aa919e1e69ede8e0a95893dbd68a145ea23b Mon Sep 17 00:00:00 2001 From: Dmitriy Dumanskiy Date: Fri, 21 Apr 2017 00:29:21 +0300 Subject: [PATCH 02/38] Removed duplicated code in ClientTest --- .../java/org/shredzone/acme4j/ClientTest.java | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java index 0b46dfd3..a812b9b0 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -76,7 +76,9 @@ public class ClientTest { */ public void fetchCertificate(Collection domains) throws IOException, AcmeException { // Load the user key file. If there is no key file, create a new one. - KeyPair userKeyPair = loadOrCreateUserKeyPair(); + // Keep this key pair in a safe place! In a production environment, you will not be + // able to access your account again if you should lose the key pair. + KeyPair userKeyPair = loadOrCreateKeyPair(USER_KEY_FILE); // Create a session for Let's Encrypt. // Use "acme://letsencrypt.org" for production server @@ -92,7 +94,7 @@ public class ClientTest { } // Load or create a key pair for the domains. This should not be the userKeyPair! - KeyPair domainKeyPair = loadOrCreateDomainKeyPair(); + KeyPair domainKeyPair = loadOrCreateKeyPair(DOMAIN_KEY_FILE); // Generate a CSR for all of the domains, and sign it with the domain key pair. CSRBuilder csrb = new CSRBuilder(); @@ -124,45 +126,19 @@ public class ClientTest { } /** - * Loads a user key pair from {@value #USER_KEY_FILE}. If the file does not exist, - * a new key pair is generated and saved. - *

- * Keep this key pair in a safe place! In a production environment, you will not be - * able to access your account again if you should lose the key pair. - * - * @return User's {@link KeyPair}. - */ - private KeyPair loadOrCreateUserKeyPair() throws IOException { - if (USER_KEY_FILE.exists()) { - // If there is a key file, read it - try (FileReader fr = new FileReader(USER_KEY_FILE)) { - return KeyPairUtils.readKeyPair(fr); - } - - } else { - // If there is none, create a new key pair and save it - KeyPair userKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE); - try (FileWriter fw = new FileWriter(USER_KEY_FILE)) { - KeyPairUtils.writeKeyPair(userKeyPair, fw); - } - return userKeyPair; - } - } - - /** - * Loads a domain key pair from {@value #DOMAIN_KEY_FILE}. If the file does not exist, + * Loads a key pair from specified file. If the file does not exist, * a new key pair is generated and saved. * - * @return Domain {@link KeyPair}. + * @return {@link KeyPair}. */ - private KeyPair loadOrCreateDomainKeyPair() throws IOException { - if (DOMAIN_KEY_FILE.exists()) { - try (FileReader fr = new FileReader(DOMAIN_KEY_FILE)) { + private KeyPair loadOrCreateKeyPair(File file) throws IOException { + if (file.exists()) { + try (FileReader fr = new FileReader(file)) { return KeyPairUtils.readKeyPair(fr); } } else { KeyPair domainKeyPair = KeyPairUtils.createKeyPair(KEY_SIZE); - try (FileWriter fw = new FileWriter(DOMAIN_KEY_FILE)) { + try (FileWriter fw = new FileWriter(file)) { KeyPairUtils.writeKeyPair(domainKeyPair, fw); } return domainKeyPair; From 608dbb6fb97d248ae20733289bfc8773e1f83ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Tue, 2 May 2017 17:24:15 +0200 Subject: [PATCH 03/38] Reduce BouncyCastle dependencies --- acme4j-utils/pom.xml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 7dea129c..8f0b3c3b 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -36,12 +36,7 @@ org.bouncycastle - bcpg-jdk15on - ${bouncycastle.version} - - - org.bouncycastle - bcmail-jdk15on + bcpkix-jdk15on ${bouncycastle.version} From 8fe25efb83e6370366802ef86882ac720c5ab41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 19 Jun 2017 22:58:02 +0200 Subject: [PATCH 04/38] Document ACME v2 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c862eacf..b2ef08c3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It is an independent open source implementation that is not affiliated with or e ## Features -* Supports ACME protocol up to [draft 02](https://tools.ietf.org/html/draft-ietf-acme-acme-02), with a few parts of [draft 03](https://tools.ietf.org/html/draft-ietf-acme-acme-03) and [draft 04](https://tools.ietf.org/html/draft-ietf-acme-acme-04) +* Supports the "ACME v1" protocol that is used by _Let's Encrypt_ * Easy to use Java API * Requires JRE 8 or higher * Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22) @@ -24,9 +24,9 @@ It is an independent open source implementation that is not affiliated with or e ## Compatibility -This version of _acme4j_ is tailor-made for _Let's Encrypt_ and other CAs that use the [Boulder](https://github.com/letsencrypt/boulder) server. Boulder [diverges from the ACME specifications](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md), so _acme4j_ cannot yet be used against other servers that fully comply to the ACME specifications. +This version of _acme4j_ is tailor-made for _Let's Encrypt_ and other CAs that use the [Boulder](https://github.com/letsencrypt/boulder) server and the "ACME v1" protocol. -The latest [ACME specifications](https://github.com/ietf-wg-acme/acme) are being implemented in the ["draft" branch](https://github.com/shred/acme4j/tree/draft) of _acme4j_. +Support for the latest [ACME v2](https://letsencrypt.org/2017/06/14/acme-v2-api.html) protocol is currently being implemented in the ["draft" branch](https://github.com/shred/acme4j/tree/draft) of _acme4j_. _Let's Encrypt_ will offer an ACME v2 API endpoint by January 2018. ## Contribute 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 05/38] 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. From 9c0247c09baffc5df81c56eb924b5b9d6d2c362f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 9 Aug 2017 19:05:54 +0200 Subject: [PATCH 06/38] Add maven central badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2ef08c3..7953e6ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ACME Java Client ![build status](https://shredzone.org/badge/acme4j.svg) +# ACME Java Client ![build status](https://shredzone.org/badge/acme4j.svg) ![maven central](https://maven-badges.herokuapp.com/maven-central/org.shredzone.acme4j/acme4j/badge.svg) This is a Java client for the [Automatic Certificate Management Environment (ACME)](https://tools.ietf.org/html/draft-ietf-acme-acme-06) protocol. From 5ad28394e984971da12d8800b163087a0023a4ad Mon Sep 17 00:00:00 2001 From: Ulf Sauer Date: Fri, 11 Aug 2017 11:37:45 +0200 Subject: [PATCH 07/38] Log response json on debug level --- .../java/org/shredzone/acme4j/connector/DefaultConnection.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 6ba604f5..998822ee 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -214,13 +214,12 @@ public class DefaultConnection implements Connection { JSON result = null; - String response = ""; try { InputStream in = conn.getResponseCode() < 400 ? conn.getInputStream() : conn.getErrorStream(); if (in != null) { result = JSON.parse(in); - LOG.debug("Result JSON: {}", response); + LOG.debug("Result JSON: {}", result); } } catch (IOException ex) { throw new AcmeNetworkException(ex); From 627a13cd934791688b7a50d5c05c7c674a00061d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sun, 13 Aug 2017 12:38:44 +0200 Subject: [PATCH 08/38] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ef9412fa..49a6a589 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ - 1.56 - 0.5.5 + 1.57 + 0.6.0 1.7.25 utf-8 From 434329c6d5cfc4926528759074697718795d4fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sun, 13 Aug 2017 13:45:27 +0200 Subject: [PATCH 09/38] Explain version numbering of ACME v2 releases --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7953e6ca..242123f2 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,18 @@ It is an independent open source implementation that is not affiliated with or e * See the [online documentation](https://shredzone.org/maven/acme4j/) about how to use _acme4j_. * For a quick start, have a look at [the source code of an example](https://github.com/shred/acme4j/blob/master/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java). -## Compatibility +## Future Compatibility This version of _acme4j_ is tailor-made for _Let's Encrypt_ and other CAs that use the [Boulder](https://github.com/letsencrypt/boulder) server and the "ACME v1" protocol. -Support for the latest [ACME v2](https://letsencrypt.org/2017/06/14/acme-v2-api.html) protocol is currently being implemented in the ["draft" branch](https://github.com/shred/acme4j/tree/draft) of _acme4j_. _Let's Encrypt_ will offer an ACME v2 API endpoint by January 2018. +ACME v1 is deprecated and will be replaced by ACME v2 in the near future. _Let's Encrypt_ [has announced](https://letsencrypt.org/2017/06/14/acme-v2-api.html) to offer an ACME v2 API endpoint by January 2018. There is no end of life date for ACME v1 at the moment. + +To prepare your software for ACME v2, you can use _acme4j_ available in the ["draft" branch](https://github.com/shred/acme4j/tree/draft), which fully supports the ACME v2 protocol, but is not downward compatible to ACME v1. A first release of this branch will be available at Maven Central in due time. + +Make sure to set the correct major version in your dependencies: + +* _acme4j_ version 0.xx → ACME v1 protocol. **This is what you want for production code at the moment.** +* _acme4j_ version 1.xx → ACME v2 protocol. The "draft" branch. Not meant for production yet. ## Contribute From 7020301940e13c37ac6a2a5c1c4ee35458a9e8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sun, 13 Aug 2017 13:46:45 +0200 Subject: [PATCH 10/38] [maven-release-plugin] prepare release v0.11 --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index e4e9d74d..dc8f7335 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11-SNAPSHOT + 0.11 acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index e86e0f0e..95dd61bf 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11-SNAPSHOT + 0.11 acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 8f0b3c3b..f0266b0e 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11-SNAPSHOT + 0.11 acme4j-utils diff --git a/pom.xml b/pom.xml index 49a6a589..6587e247 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.11-SNAPSHOT + 0.11 pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - HEAD + v0.11 GitHub From beb7c8b74501720151c40179944bea91587bfe4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sun, 13 Aug 2017 13:46:45 +0200 Subject: [PATCH 11/38] [maven-release-plugin] prepare for next development iteration --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index dc8f7335..de4c4116 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11 + 0.12-SNAPSHOT acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 95dd61bf..b308045e 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11 + 0.12-SNAPSHOT acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index f0266b0e..337e639b 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.11 + 0.12-SNAPSHOT acme4j-utils diff --git a/pom.xml b/pom.xml index 6587e247..d33f1ab5 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.11 + 0.12-SNAPSHOT pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - v0.11 + HEAD GitHub From 658bfa2cdafadb04cdfb5d86c0c9581bb4340b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 11 Sep 2017 19:06:03 +0200 Subject: [PATCH 12/38] Add Problem object for JSON problem documents Backport of commit 4e1d173c, fdc05ba7 --- .../java/org/shredzone/acme4j/Problem.java | 70 +++++++++++++++++++ .../java/org/shredzone/acme4j/util/JSON.java | 14 ++++ .../org/shredzone/acme4j/ProblemTest.java | 43 ++++++++++++ .../org/shredzone/acme4j/util/JSONTest.java | 16 ++++- .../src/test/resources/json.properties | 9 ++- 5 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java create mode 100644 acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java new file mode 100644 index 00000000..dc869226 --- /dev/null +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java @@ -0,0 +1,70 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import java.io.Serializable; +import java.net.URI; + +import org.shredzone.acme4j.util.JSON; + +/** + * Represents a JSON Problem. + * + * @see RFC 7807 + */ +public class Problem implements Serializable { + private static final long serialVersionUID = -8418248862966754214L; + + private final JSON problemJson; + + /** + * Creates a new {@link Problem} object. + * + * @param problem + * Problem as JSON structure + */ + public Problem(JSON problem) { + this.problemJson = problem; + } + + /** + * Returns the problem type. + */ + public URI getType() { + return problemJson.get("type").asURI(); + } + + /** + * Returns a human-readable description of the problem. + */ + public String getDetail() { + return problemJson.get("detail").asString(); + } + + /** + * Returns the problem as {@link JSON} object, to access other fields. + */ + public JSON asJSON() { + return problemJson; + } + + /** + * Returns the problem as JSON string. + */ + @Override + public String toString() { + return problemJson.toString(); + } + +} diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java index fcdb7f98..c0a57b7c 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java @@ -41,6 +41,7 @@ import java.util.stream.StreamSupport; import org.jose4j.json.JsonUtil; import org.jose4j.lang.JoseException; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.exception.AcmeProtocolException; /** @@ -294,6 +295,19 @@ public final class JSON implements Serializable { } } + /** + * Returns the value as {@link Problem}. + * + * @return {@link Problem}, or {@code null} if the value was not set. + */ + public Problem asProblem() { + if (val == null) { + return null; + } + + return new Problem(asObject()); + } + /** * Returns the value as JSON array. * diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java new file mode 100644 index 00000000..7edb467c --- /dev/null +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java @@ -0,0 +1,43 @@ +/* + * acme4j - Java ACME client + * + * Copyright (C) 2017 Richard "Shred" Körber + * http://acme4j.shredzone.org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ +package org.shredzone.acme4j; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +import java.net.URI; + +import org.junit.Test; +import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.util.TestUtils; + +/** + * Unit tests for {@link Problem}. + */ +public class ProblemTest { + + @Test + public void testProblem() { + JSON original = TestUtils.getJsonAsObject("problem"); + + Problem problem = new Problem(original); + + assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); + assertThat(problem.getDetail(), is("connection refused")); + assertThat(problem.asJSON().toString(), is(sameJSONAs(original.toString()))); + assertThat(problem.toString(), is(sameJSONAs(original.toString()))); + } + +} diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java index c828ae6c..2ea4f57c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java @@ -36,6 +36,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import org.junit.Test; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.exception.AcmeProtocolException; /** @@ -84,7 +85,7 @@ public class JSONTest { JSON json = TestUtils.getJsonAsObject("json"); assertThat(json.keySet(), containsInAnyOrder( - "text", "number", "uri", "url", "date", "array", "collect")); + "text", "number", "uri", "url", "date", "array", "collect", "problem")); assertThat(json.contains("text"), is(true)); assertThat(json.contains("music"), is(false)); assertThat(json.get("text"), is(notNullValue())); @@ -189,6 +190,11 @@ public class JSONTest { JSON sub = array.get(3).asObject(); assertThat(sub.get("test").asString(), is("ok")); + + Problem problem = json.get("problem").asProblem(); + assertThat(problem, is(notNullValue())); + assertThat(problem.getType(), is(URI.create("urn:ietf:params:acme:error:rateLimited"))); + assertThat(problem.getDetail(), is("too many requests")); } /** @@ -205,6 +211,7 @@ public class JSONTest { assertThat(json.get("none").asInstant(), is(nullValue())); assertThat(json.get("none").asArray(), is(nullValue())); assertThat(json.get("none").asObject(), is(nullValue())); + assertThat(json.get("none").asProblem(), is(nullValue())); try { json.get("none").asInt(); @@ -272,6 +279,13 @@ public class JSONTest { } catch (AcmeProtocolException ex) { // expected } + + try { + json.get("text").asProblem(); + fail("no exception was thrown"); + } catch (AcmeProtocolException ex) { + // expected + } } /** diff --git a/acme4j-client/src/test/resources/json.properties b/acme4j-client/src/test/resources/json.properties index c46e727d..f38344f6 100644 --- a/acme4j-client/src/test/resources/json.properties +++ b/acme4j-client/src/test/resources/json.properties @@ -42,7 +42,14 @@ json = \ "url": "http://example.com",\ "date": "2016-01-08T00:00:00Z",\ "array": ["foo", 987, [1, 2, 3], {"test": "ok"}],\ - "collect": ["foo", "bar", "barfoo"]\ + "collect": ["foo", "bar", "barfoo"],\ + "problem": {"type": "urn:ietf:params:acme:error:rateLimited", "detail": "too many requests"}\ + } + +problem = \ + {\ + "type": "urn:ietf:params:acme:error:connection",\ + "detail": "connection refused"\ } newRegistration = \ From 7c2c88add0f037e4e92c14cf5625ccad9273f4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 11 Sep 2017 23:01:32 +0200 Subject: [PATCH 13/38] Challenges return error state Partial backport of commit cdb23628. Fixes issue #35. --- .../java/org/shredzone/acme4j/challenge/Challenge.java | 9 +++++++++ .../org/shredzone/acme4j/challenge/ChallengeTest.java | 3 +++ acme4j-client/src/test/resources/json.properties | 6 +++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java index 5e235f61..ec0dcb4b 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java @@ -19,6 +19,7 @@ import java.time.Instant; import java.util.Objects; import org.shredzone.acme4j.AcmeResource; +import org.shredzone.acme4j.Problem; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; import org.shredzone.acme4j.connector.Connection; @@ -47,6 +48,7 @@ public class Challenge extends AcmeResource { protected static final String KEY_STATUS = "status"; protected static final String KEY_URI = "uri"; protected static final String KEY_VALIDATED = "validated"; + protected static final String KEY_ERROR = "error"; private JSON data = JSON.empty(); @@ -117,6 +119,13 @@ public class Challenge extends AcmeResource { return data.get(KEY_VALIDATED).asInstant(); } + /** + * Returns the reason why the challenge failed, if returned by the server. + */ + public Problem getError() { + return data.get(KEY_ERROR).asProblem(); + } + /** * Returns the JSON representation of the challenge data. */ diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java index 68ffe4ae..68340ec7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java @@ -113,6 +113,9 @@ public class ChallengeTest { assertThat(challenge.getStatus(), is(Status.VALID)); assertThat(challenge.getLocation(), is(new URI("http://example.com/challenge/123"))); assertThat(challenge.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z"))); + assertThat(challenge.getError(), is(notNullValue())); + assertThat(challenge.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); + assertThat(challenge.getError().getDetail(), is("connection refused")); assertThat(challenge.getJSON().get("type").asString(), is("generic-01")); assertThat(challenge.getJSON().get("uri").asURL(), is(new URL("http://example.com/challenge/123"))); assertThat(challenge.getJSON().get("not-present").asString(), is(nullValue())); diff --git a/acme4j-client/src/test/resources/json.properties b/acme4j-client/src/test/resources/json.properties index f38344f6..201e60fc 100644 --- a/acme4j-client/src/test/resources/json.properties +++ b/acme4j-client/src/test/resources/json.properties @@ -225,7 +225,11 @@ genericChallenge = \ "type":"generic-01", \ "status":"valid", \ "uri":"http://example.com/challenge/123", \ - "validated":"2015-12-12T17:19:36.336785823Z" \ + "validated":"2015-12-12T17:19:36.336785823Z", \ + "error": { \ + "type": "urn:ietf:params:acme:error:connection", \ + "detail": "connection refused" \ + }\ } httpChallenge = \ From 0548fda9fd48c92459ef5d41539e6fd0a6d43b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 20 Sep 2017 20:53:26 +0200 Subject: [PATCH 14/38] Do not use compact JWS serialization Backport of commit 2eb59ef3 --- .../acme4j/connector/DefaultConnection.java | 8 +++++++- .../connector/DefaultConnectionTest.java | 19 +++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 998822ee..31f423a5 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -156,7 +156,13 @@ public class DefaultConnection implements Connection { jws.getHeaders().setJwkHeaderValue("jwk", jwk); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setKey(keypair.getPrivate()); - byte[] outputData = jws.getCompactSerialization().getBytes(DEFAULT_CHARSET); + jws.sign(); + + JSONBuilder jb = new JSONBuilder(); + jb.put("protected", jws.getHeaders().getEncodedHeader()); + jb.put("payload", jws.getEncodedPayload()); + jb.put("signature", jws.getEncodedSignature()); + byte[] outputData = jb.toString().getBytes(DEFAULT_CHARSET); conn.setFixedLengthStreamingMode(outputData.length); conn.connect(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index a1ab6e94..244eb0b4 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -542,11 +542,10 @@ public class DefaultConnectionTest { verify(mockUrlConnection, atLeast(0)).getHeaderFields(); verifyNoMoreInteractions(mockUrlConnection); - String serialized = new String(outputStream.toByteArray(), "utf-8"); - String[] written = CompactSerializer.deserialize(serialized); - String header = Base64Url.decodeToUtf8String(written[0]); - String claims = Base64Url.decodeToUtf8String(written[1]); - String signature = written[2]; + JSON data = JSON.parse(new String(outputStream.toByteArray(), "utf-8")); + String encodedHeader = data.get("protected").asString(); + String encodedSignature = data.get("signature").asString(); + String encodedPayload = data.get("payload").asString(); StringBuilder expectedHeader = new StringBuilder(); expectedHeader.append('{'); @@ -559,13 +558,13 @@ public class DefaultConnectionTest { expectedHeader.append("\"n\":\"").append(TestUtils.N).append("\""); expectedHeader.append("}}"); - assertThat(header, sameJSONAs(expectedHeader.toString())); - assertThat(claims, sameJSONAs("{\"foo\":123,\"bar\":\"a-string\"}")); - assertThat(signature, not(isEmptyOrNullString())); + assertThat(Base64Url.decodeToUtf8String(encodedHeader), sameJSONAs(expectedHeader.toString())); + assertThat(Base64Url.decodeToUtf8String(encodedPayload), sameJSONAs("{\"foo\":123,\"bar\":\"a-string\"}")); + assertThat(encodedSignature, not(isEmptyOrNullString())); JsonWebSignature jws = new JsonWebSignature(); - jws.setCompactSerialization(serialized); - jws.setKey(DefaultConnectionTest.this.session.getKeyPair().getPublic()); + jws.setCompactSerialization(CompactSerializer.serialize(encodedHeader, encodedPayload, encodedSignature)); + jws.setKey(session.getKeyPair().getPublic()); assertThat(jws.verifySignature(), is(true)); } From 0f5b0e7b037208a3c1f1c54b40e50b068594ccd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 20 Sep 2017 20:53:35 +0200 Subject: [PATCH 15/38] Disable hardcoded Let's Encrypt server certificate --- .../letsencrypt/LetsEncryptAcmeProvider.java | 15 ++++++++++++--- .../letsencrypt/LetsEncryptHttpConnector.java | 4 ++++ src/site/markdown/ca/letsencrypt.md | 8 ++------ src/site/markdown/migration.md | 6 ++++++ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java index 98bdeb50..d2dee002 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java @@ -20,6 +20,8 @@ import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.provider.AbstractAcmeProvider; import org.shredzone.acme4j.provider.AcmeProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * An {@link AcmeProvider} for Let's Encrypt. @@ -27,13 +29,14 @@ import org.shredzone.acme4j.provider.AcmeProvider; * The {@code serverUri} is {@code "acme://letsencrypt.org"} for the production server, * and {@code "acme://letsencrypt.org/staging"} for a testing server. *

- * If you want to use Let's Encrypt, always prefer to use this provider, as it - * takes care for the correct connection and SSL certificates. + * If you want to use Let's Encrypt, always prefer to use this provider. * * @see Let's Encrypt */ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { + private static final Logger LOG = LoggerFactory.getLogger(LetsEncryptAcmeProvider.class); + private static final String V01_DIRECTORY_URI = "https://acme-v01.api.letsencrypt.org/directory"; private static final String STAGING_DIRECTORY_URI = "https://acme-staging.api.letsencrypt.org/directory"; @@ -63,8 +66,14 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { } @Override + @SuppressWarnings("deprecation") protected HttpConnector createHttpConnector() { - return new LetsEncryptHttpConnector(); + if (Boolean.getBoolean("acme4j.le.certfix")) { + LOG.warn("Using a hardcoded Let's Encrypt certificate. It will expire by June 2018."); + return new LetsEncryptHttpConnector(); + } else { + return super.createHttpConnector(); + } } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java index a8972a51..1f719561 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java @@ -32,7 +32,11 @@ import org.shredzone.acme4j.connector.HttpConnector; /** * {@link HttpConnector} to be used for Let's Encrypt. It is pinned to the Let's Encrypt * server certificate. + * + * @deprecated API server certificate CA is accepted by current JREs. There is no need + * for certificate pinning any more. Hardcoded certificate will expire by June 25th, 2018. */ +@Deprecated public class LetsEncryptHttpConnector extends HttpConnector { private static SSLSocketFactory sslSocketFactory; diff --git a/src/site/markdown/ca/letsencrypt.md b/src/site/markdown/ca/letsencrypt.md index 0f9d19d5..f2b2c43c 100644 --- a/src/site/markdown/ca/letsencrypt.md +++ b/src/site/markdown/ca/letsencrypt.md @@ -8,13 +8,9 @@ Web site: [Let's Encrypt](https://letsencrypt.org) * `acme://letsencrypt.org/staging` - Testing server * `acme://letsencrypt.org/v01` - Production server, pinned to API v01 -## Features +## Compatibility -* Accepts the ACME server certificate of Let's Encrypt even on older Java versions - -## Note - -* _Let's Encrypt_ diverges from the ACME specifications for various reasons. For this reason, some parts of the _acme4j_ API may not behave as documented. [See here for more details.](https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md) +If you have to use a Java version that is older than 8u101 and does not accept the _IdenTrust_ certificates of the _Let's Encrypt_ servers, you can use a hardcoded local truststore as a workaround by setting the `acme4j.le.certfix` system property to `true`. Please note that the hardwired certificate will expire by June, 2018. ## Limits diff --git a/src/site/markdown/migration.md b/src/site/markdown/migration.md index ea06ee79..16147eff 100644 --- a/src/site/markdown/migration.md +++ b/src/site/markdown/migration.md @@ -2,6 +2,12 @@ This document will help you migrate your code to the latest _acme4j_ version. +## Migration to Version 0.12 + +Java support for the _IdenTrust_ certificate that is used by _Let's Encrypt_ servers was added to JRE 8u101 in July 2016. For this reason, _acme4j_ does not need to use a hardcoded local truststore anymore. It has been disabled in this version, and the standard Java truststore is used instead. + +If you have to use an older JRE, you can re-enable the local truststore by setting the `acme4j.le.certfix` system property to `true`. Please note that the local certificate will expire by June, 2018. + ## Migration to Version 0.10 Starting with version 0.10, _acme4j_ requires Java 8 or higher. This is also reflected in the API. From c079db192caa62cdb51995a3a07a6590749407bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 20 Sep 2017 21:06:53 +0200 Subject: [PATCH 16/38] [maven-release-plugin] prepare release v0.12 --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index de4c4116..98d111e8 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12-SNAPSHOT + 0.12 acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index b308045e..6b31812d 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12-SNAPSHOT + 0.12 acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 337e639b..fa867371 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12-SNAPSHOT + 0.12 acme4j-utils diff --git a/pom.xml b/pom.xml index d33f1ab5..20fa539d 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.12-SNAPSHOT + 0.12 pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - HEAD + v0.12 GitHub From a7ce6a5f086b71391e1006915c7b693ed01ef693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 20 Sep 2017 21:06:53 +0200 Subject: [PATCH 17/38] [maven-release-plugin] prepare for next development iteration --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index 98d111e8..b6fc000d 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12 + 0.13-SNAPSHOT acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 6b31812d..4446c546 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12 + 0.13-SNAPSHOT acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index fa867371..327e623a 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.12 + 0.13-SNAPSHOT acme4j-utils diff --git a/pom.xml b/pom.xml index 20fa539d..978b8404 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.12 + 0.13-SNAPSHOT pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - v0.12 + HEAD GitHub From 8f7b45a69f0e8ecb06094a0a592332bbcee1fd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 30 Sep 2017 18:03:08 +0200 Subject: [PATCH 18/38] Fix unit tests for Java 9 --- .../acme4j/connector/DefaultConnectionTest.java | 3 ++- .../org/shredzone/acme4j/util/AcmeUtilsTest.java | 16 ++-------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index 244eb0b4..a204b373 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -27,6 +27,7 @@ import java.net.URL; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -255,7 +256,7 @@ public class DefaultConnectionTest { */ @Test public void testHandleRetryAfterHeaderDate() throws AcmeException, IOException { - Instant retryDate = Instant.now().plus(Duration.ofHours(10)); + Instant retryDate = Instant.now().plus(Duration.ofHours(10)).truncatedTo(ChronoUnit.MILLIS); String retryMsg = "absolute date"; when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_ACCEPTED); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java index 5452c590..2cf59b60 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java @@ -26,8 +26,6 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import javax.xml.bind.DatatypeConverter; - import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -57,20 +55,10 @@ public class AcmeUtilsTest { } /** - * Test sha-256 hash. + * Test sha-256 hash and hex encode. */ @Test - public void testSha256Hash() { - byte[] hash = sha256hash("foobar"); - byte[] expected = DatatypeConverter.parseHexBinary("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"); - assertThat(hash, is(expected)); - } - - /** - * Test hex encode. - */ - @Test - public void testHexEncode() { + public void testSha256HashHexEncode() { String hexEncode = hexEncode(sha256hash("foobar")); assertThat(hexEncode, is("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2")); } From a87e35b311cac1646ab31405d7f6e2d7cb980319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 30 Sep 2017 18:12:35 +0200 Subject: [PATCH 19/38] Rename internal util package in client module Avoids a split package in Java 9. Fixes issue #37. --- .../main/java/org/shredzone/acme4j/Authorization.java | 6 +++--- .../main/java/org/shredzone/acme4j/Certificate.java | 2 +- .../src/main/java/org/shredzone/acme4j/Metadata.java | 6 +++--- .../src/main/java/org/shredzone/acme4j/Problem.java | 2 +- .../main/java/org/shredzone/acme4j/Registration.java | 6 +++--- .../java/org/shredzone/acme4j/RegistrationBuilder.java | 2 +- .../src/main/java/org/shredzone/acme4j/Session.java | 2 +- .../java/org/shredzone/acme4j/challenge/Challenge.java | 4 ++-- .../org/shredzone/acme4j/challenge/Dns01Challenge.java | 2 +- .../shredzone/acme4j/challenge/TlsSni01Challenge.java | 2 +- .../shredzone/acme4j/challenge/TlsSni02Challenge.java | 2 +- .../org/shredzone/acme4j/challenge/TokenChallenge.java | 4 ++-- .../org/shredzone/acme4j/connector/Connection.java | 4 ++-- .../shredzone/acme4j/connector/DefaultConnection.java | 8 ++++---- .../shredzone/acme4j/connector/ResourceIterator.java | 2 +- .../acme4j/exception/AcmeServerException.java | 2 +- .../acme4j/provider/AbstractAcmeProvider.java | 2 +- .../org/shredzone/acme4j/provider/AcmeProvider.java | 2 +- .../shredzone/acme4j/{util => toolbox}/AcmeUtils.java | 2 +- .../org/shredzone/acme4j/{util => toolbox}/JSON.java | 4 ++-- .../acme4j/{util => toolbox}/JSONBuilder.java | 4 ++-- .../java/org/shredzone/acme4j/AcmeResourceTest.java | 2 +- .../java/org/shredzone/acme4j/AuthorizationTest.java | 8 ++++---- .../java/org/shredzone/acme4j/CertificateTest.java | 6 +++--- .../test/java/org/shredzone/acme4j/ProblemTest.java | 4 ++-- .../org/shredzone/acme4j/RegistrationBuilderTest.java | 4 ++-- .../java/org/shredzone/acme4j/RegistrationTest.java | 8 ++++---- .../test/java/org/shredzone/acme4j/SessionTest.java | 8 ++++---- .../org/shredzone/acme4j/challenge/ChallengeTest.java | 10 +++++----- .../shredzone/acme4j/challenge/DnsChallengeTest.java | 6 +++--- .../shredzone/acme4j/challenge/HttpChallengeTest.java | 6 +++--- .../acme4j/challenge/OutOfBandChallengeTest.java | 6 +++--- .../acme4j/challenge/TlsSni01ChallengeTest.java | 6 +++--- .../acme4j/challenge/TlsSni02ChallengeTest.java | 6 +++--- .../acme4j/connector/DefaultConnectionTest.java | 6 +++--- .../shredzone/acme4j/connector/DummyConnection.java | 4 ++-- .../acme4j/connector/ResourceIteratorTest.java | 6 +++--- .../acme4j/connector/SessionProviderTest.java | 4 ++-- .../acme4j/provider/AbstractAcmeProviderTest.java | 6 +++--- .../acme4j/provider/TestableConnectionProvider.java | 6 +++--- .../acme4j/{util => toolbox}/AcmeUtilsTest.java | 4 ++-- .../acme4j/{util => toolbox}/JSONBuilderTest.java | 2 +- .../shredzone/acme4j/{util => toolbox}/JSONTest.java | 2 +- .../shredzone/acme4j/{util => toolbox}/TestUtils.java | 2 +- .../java/org/shredzone/acme4j/util/CSRBuilder.java | 2 +- 45 files changed, 97 insertions(+), 97 deletions(-) rename acme4j-client/src/main/java/org/shredzone/acme4j/{util => toolbox}/AcmeUtils.java (99%) rename acme4j-client/src/main/java/org/shredzone/acme4j/{util => toolbox}/JSON.java (99%) rename acme4j-client/src/main/java/org/shredzone/acme4j/{util => toolbox}/JSONBuilder.java (97%) rename acme4j-client/src/test/java/org/shredzone/acme4j/{util => toolbox}/AcmeUtilsTest.java (99%) rename acme4j-client/src/test/java/org/shredzone/acme4j/{util => toolbox}/JSONBuilderTest.java (99%) rename acme4j-client/src/test/java/org/shredzone/acme4j/{util => toolbox}/JSONTest.java (99%) rename acme4j-client/src/test/java/org/shredzone/acme4j/{util => toolbox}/TestUtils.java (99%) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java index 74e95002..0cd7af81 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j; import static java.util.stream.Collectors.toList; -import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp; +import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; import java.net.HttpURLConnection; import java.net.URI; @@ -30,8 +30,8 @@ import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 028404fc..d7caf6cd 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -25,7 +25,7 @@ import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java index 6448fb86..297e0ec5 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Metadata.java @@ -19,9 +19,9 @@ import java.net.URI; import java.util.Collection; import java.util.Collections; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSON.Array; -import org.shredzone.acme4j.util.JSON.Value; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSON.Array; +import org.shredzone.acme4j.toolbox.JSON.Value; /** * Contains metadata related to the provider. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java index dc869226..b9bbe8b9 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j; import java.io.Serializable; import java.net.URI; -import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.toolbox.JSON; /** * Represents a JSON Problem. 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 dc50897e..2a6b7eea 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j; -import static org.shredzone.acme4j.util.AcmeUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import java.net.HttpURLConnection; import java.net.URI; @@ -36,8 +36,8 @@ import org.shredzone.acme4j.connector.ResourceIterator; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java index 1acf1171..93dceff4 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java @@ -22,7 +22,7 @@ import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeConflictException; import org.shredzone.acme4j.exception.AcmeException; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java index e0843107..2984be37 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java @@ -30,7 +30,7 @@ import org.shredzone.acme4j.challenge.TokenChallenge; import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.provider.AcmeProvider; -import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.toolbox.JSON; /** * A session stores the ACME server URI and the account's key pair. It also tracks diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java index ec0dcb4b..8534174c 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java @@ -26,8 +26,8 @@ import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java index ae2f84d7..1925b6ea 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Dns01Challenge.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.challenge; -import static org.shredzone.acme4j.util.AcmeUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import org.shredzone.acme4j.Session; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java index 6ab04f5f..cbb88814 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.challenge; -import static org.shredzone.acme4j.util.AcmeUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import org.shredzone.acme4j.Session; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java index 63cc5852..03404655 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni02Challenge.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.challenge; -import static org.shredzone.acme4j.util.AcmeUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import org.shredzone.acme4j.Session; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java index 2e9d9bed..f751282d 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TokenChallenge.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.challenge; -import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode; +import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode; import java.security.PublicKey; @@ -21,7 +21,7 @@ import org.jose4j.jwk.PublicJsonWebKey; import org.jose4j.lang.JoseException; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeProtocolException; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * An extension of {@link Challenge} that handles challenges with a {@code token} and diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java index 7de36424..c1240675 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java @@ -20,8 +20,8 @@ import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Connects to the ACME server and offers different methods for invoking the API. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 31f423a5..5e1e8f9e 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.connector; -import static org.shredzone.acme4j.util.AcmeUtils.keyAlgorithm; +import static org.shredzone.acme4j.toolbox.AcmeUtils.keyAlgorithm; import java.io.IOException; import java.io.InputStream; @@ -52,9 +52,9 @@ import org.shredzone.acme4j.exception.AcmeRateLimitExceededException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.exception.AcmeServerException; import org.shredzone.acme4j.exception.AcmeUnauthorizedException; -import org.shredzone.acme4j.util.AcmeUtils; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.AcmeUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java index 0ef74d32..71f37f54 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java @@ -26,7 +26,7 @@ import org.shredzone.acme4j.AcmeResource; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; -import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.toolbox.JSON; /** * An {@link Iterator} that fetches a batch of URIs from the ACME server, and generates diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java index 3adc4f14..f00dd3f8 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeServerException.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.exception; import java.util.Objects; -import org.shredzone.acme4j.util.AcmeUtils; +import org.shredzone.acme4j.toolbox.AcmeUtils; /** * An exception that is thrown when the ACME server returned an error. It contains diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java index 1d1d224e..cbc9e7a3 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java @@ -31,7 +31,7 @@ import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.DefaultConnection; import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.exception.AcmeException; -import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.toolbox.JSON; /** * Abstract implementation of {@link AcmeProvider}. It consists of a challenge diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java index 529de286..c563ad2a 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java @@ -20,7 +20,7 @@ import org.shredzone.acme4j.Session; import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.exception.AcmeException; -import org.shredzone.acme4j.util.JSON; +import org.shredzone.acme4j.toolbox.JSON; /** * An {@link AcmeProvider} provides methods to be used for communicating with the ACME diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java similarity index 99% rename from acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java rename to acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java index dedd9d9a..6e574f32 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/AcmeUtils.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java @@ -11,7 +11,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import java.io.UnsupportedEncodingException; import java.net.IDN; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSON.java similarity index 99% rename from acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java rename to acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSON.java index c0a57b7c..1b925672 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSON.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSON.java @@ -11,10 +11,10 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import static java.util.stream.Collectors.joining; -import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp; +import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; import java.io.BufferedReader; import java.io.IOException; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSONBuilder.java similarity index 97% rename from acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java rename to acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSONBuilder.java index f4a3874e..9534e3b2 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/JSONBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/JSONBuilder.java @@ -11,9 +11,9 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; -import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode; +import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode; import java.security.Key; import java.security.PublicKey; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java index fa4155a9..27c32987 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java @@ -23,7 +23,7 @@ import java.io.ObjectOutputStream; import java.net.URI; import org.junit.Test; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link AcmeResource}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index 09db84b7..182b7597 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -15,8 +15,8 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp; -import static org.shredzone.acme4j.util.TestUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; @@ -34,8 +34,8 @@ import org.shredzone.acme4j.challenge.TlsSni02Challenge; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Unit tests for {@link Authorization}. 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 f5b7ee40..7d391f1f 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.shredzone.acme4j.util.TestUtils.*; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -31,8 +31,8 @@ import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Certificate}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java index 7edb467c..15fa82f2 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/ProblemTest.java @@ -20,8 +20,8 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.URI; import org.junit.Test; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Problem}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java index ddc841cc..e60fbb0c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.*; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; @@ -25,7 +25,7 @@ import org.junit.Test; import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Unit tests for {@link RegistrationBuilder}. 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 b4508b74..5663218c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.shredzone.acme4j.util.TestUtils.*; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -42,9 +42,9 @@ import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.provider.AcmeProvider; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Registration}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java index 46311ad6..02591044 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import java.io.IOException; import java.net.URI; @@ -30,9 +30,9 @@ import org.shredzone.acme4j.challenge.Http01Challenge; import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.provider.AcmeProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit test for {@link Session}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java index 68340ec7..5c6ec946 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java @@ -15,8 +15,8 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.shredzone.acme4j.util.AcmeUtils.parseTimestamp; -import static org.shredzone.acme4j.util.TestUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -37,9 +37,9 @@ import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java index 5c36bf9c..c1ae33e5 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/DnsChallengeTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -24,8 +24,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Dns01Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java index 0e048069..c244ece3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/HttpChallengeTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -25,8 +25,8 @@ import org.junit.Test; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; import org.shredzone.acme4j.exception.AcmeProtocolException; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Http01Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/OutOfBandChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/OutOfBandChallengeTest.java index dd98d2b2..d4604b66 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/OutOfBandChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/OutOfBandChallengeTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -25,8 +25,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link OutOfBand01Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java index ace8014d..f317bb84 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -24,8 +24,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link TlsSni01Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java index e04b201a..759a495c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni02ChallengeTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.challenge; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; @@ -24,8 +24,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Status; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link TlsSni02Challenge}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index a204b373..13ba9c3a 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -47,9 +47,9 @@ import org.shredzone.acme4j.exception.AcmeNetworkException; import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.exception.AcmeRetryAfterException; import org.shredzone.acme4j.exception.AcmeServerException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link DefaultConnection}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java index 9349fea0..e58574d7 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java @@ -19,8 +19,8 @@ import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Dummy implementation of {@link Connection} that always fails. Single methods are diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java index 8a87b193..1bb6059f 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.connector; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.util.TestUtils.isIntArrayContainingInAnyOrder; +import static org.shredzone.acme4j.toolbox.TestUtils.isIntArrayContainingInAnyOrder; import java.io.IOException; import java.net.HttpURLConnection; @@ -31,8 +31,8 @@ import org.shredzone.acme4j.Authorization; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.provider.TestableConnectionProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.JSONBuilder; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; /** * Unit test for {@link ResourceIterator}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java index ae66b5d2..694af22d 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java @@ -27,8 +27,8 @@ import org.shredzone.acme4j.Session; import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.provider.AcmeProvider; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link Session#provider()}. Requires that both enclosed diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java index 83be73a3..b233d4c1 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java @@ -17,7 +17,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.util.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; @@ -34,8 +34,8 @@ import org.shredzone.acme4j.challenge.TlsSni02Challenge; import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.DefaultConnection; import org.shredzone.acme4j.connector.HttpConnector; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link AbstractAcmeProvider}. diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java index 590d71a6..f0d7566b 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java @@ -24,9 +24,9 @@ import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.DummyConnection; import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.exception.AcmeException; -import org.shredzone.acme4j.util.JSONBuilder; -import org.shredzone.acme4j.util.JSON; -import org.shredzone.acme4j.util.TestUtils; +import org.shredzone.acme4j.toolbox.JSON; +import org.shredzone.acme4j.toolbox.JSONBuilder; +import org.shredzone.acme4j.toolbox.TestUtils; /** * Test implementation of {@link AcmeProvider}. It also implements a dummy implementation diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java similarity index 99% rename from acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java rename to acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java index 2cf59b60..70c338e3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/AcmeUtilsTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java @@ -11,11 +11,11 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.shredzone.acme4j.util.AcmeUtils.*; +import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONBuilderTest.java similarity index 99% rename from acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java rename to acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONBuilderTest.java index 0457338e..d984f05c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONBuilderTest.java @@ -11,7 +11,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONTest.java similarity index 99% rename from acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java rename to acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONTest.java index 2ea4f57c..8cf444a5 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/JSONTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/JSONTest.java @@ -11,7 +11,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java similarity index 99% rename from acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java rename to acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java index 1a36e977..560a600c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/TestUtils.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java @@ -11,7 +11,7 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ -package org.shredzone.acme4j.util; +package org.shredzone.acme4j.toolbox; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; diff --git a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java index 2f790343..f8ab0126 100644 --- a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java +++ b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CSRBuilder.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j.util; import static java.util.stream.Collectors.joining; -import static org.shredzone.acme4j.util.AcmeUtils.toAce; +import static org.shredzone.acme4j.toolbox.AcmeUtils.toAce; import java.io.IOException; import java.io.OutputStream; From 6f2d7bc4217129f5ba477c747fac3392a4d6a0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 4 Oct 2017 00:29:25 +0200 Subject: [PATCH 20/38] Update dependencies --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 978b8404..7e75ed12 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,8 @@ - 1.57 - 0.6.0 + 1.58 + 0.6.1 1.7.25 utf-8 From 3e63f7d34dd8ced9c4786fe7e49d0e913612d325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 4 Oct 2017 00:34:06 +0200 Subject: [PATCH 21/38] Document renamed package in migration guide --- src/site/markdown/migration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/site/markdown/migration.md b/src/site/markdown/migration.md index 16147eff..5e0ed548 100644 --- a/src/site/markdown/migration.md +++ b/src/site/markdown/migration.md @@ -2,6 +2,10 @@ This document will help you migrate your code to the latest _acme4j_ version. +## Migration to Version 0.13 + +In the `acme4j-client` module, the `org.shredzone.acme4j.util` package has been renamed in order to fix a split package in Java 9. This package is meant to be internal, so this change should not break your code. The same named public package in the `acme4j-utils` module is unchanged. + ## Migration to Version 0.12 Java support for the _IdenTrust_ certificate that is used by _Let's Encrypt_ servers was added to JRE 8u101 in July 2016. For this reason, _acme4j_ does not need to use a hardcoded local truststore anymore. It has been disabled in this version, and the standard Java truststore is used instead. From 7440919e614293866120ebf4eff048e7ebdc55ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 4 Oct 2017 16:20:26 +0200 Subject: [PATCH 22/38] [maven-release-plugin] prepare release v0.13 --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index b6fc000d..d95bdf7b 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13-SNAPSHOT + 0.13 acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 4446c546..51a7ffa2 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13-SNAPSHOT + 0.13 acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 327e623a..5ea8ce61 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13-SNAPSHOT + 0.13 acme4j-utils diff --git a/pom.xml b/pom.xml index 7e75ed12..9998a4cb 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.13-SNAPSHOT + 0.13 pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - HEAD + v0.13 GitHub From 8535a1219be8ff2bc5661f792729743797b665b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 4 Oct 2017 16:20:26 +0200 Subject: [PATCH 23/38] [maven-release-plugin] prepare for next development iteration --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index d95bdf7b..8fc05377 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13 + 0.14-SNAPSHOT acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 51a7ffa2..8d684907 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13 + 0.14-SNAPSHOT acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 5ea8ce61..86b069d4 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.13 + 0.14-SNAPSHOT acme4j-utils diff --git a/pom.xml b/pom.xml index 9998a4cb..1871a88d 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.13 + 0.14-SNAPSHOT pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - v0.13 + HEAD GitHub From d666ec091cf5cf8a2af4265c5e78f6d7b7d524f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Tue, 24 Oct 2017 23:12:17 +0200 Subject: [PATCH 24/38] Mention optional Bouncy Castle dependency Closes PR #38 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 242123f2..291e3e2b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ It is an independent open source implementation that is not affiliated with or e * Easy to use Java API * Requires JRE 8 or higher * Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22) -* Small, only requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home) and [slf4j](http://www.slf4j.org/) as dependencies +* Small: `acme4j-client` only requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home) and [slf4j](http://www.slf4j.org/) as dependencies +* Only the optional `acme4j-utils` module requires [Bouncy Castle](http://www.bouncycastle.org) * Extensive unit tests ## Usage From 1289e2f5e81f16affbadda1981f7ad1ed91abee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Thu, 2 Nov 2017 23:19:31 +0100 Subject: [PATCH 25/38] Accept Content-Type header with charset parameter Backport of 42541ac2 --- .../acme4j/connector/DefaultConnection.java | 7 +++-- .../shredzone/acme4j/toolbox/AcmeUtils.java | 26 ++++++++++++++++ .../acme4j/toolbox/AcmeUtilsTest.java | 30 +++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 5e1e8f9e..245893d4 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -192,7 +192,8 @@ public class DefaultConnection implements Connection { return match.getAsInt(); } - if (!"application/problem+json".equals(conn.getHeaderField(CONTENT_TYPE_HEADER))) { + String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER)); + if (!"application/problem+json".equals(contentType)) { throw new AcmeException("HTTP " + rc + ": " + conn.getResponseMessage()); } @@ -212,7 +213,7 @@ public class DefaultConnection implements Connection { public JSON readJsonResponse() throws AcmeException { assertConnectionIsOpen(); - String contentType = conn.getHeaderField(CONTENT_TYPE_HEADER); + String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER)); if (!("application/json".equals(contentType) || "application/problem+json".equals(contentType))) { throw new AcmeProtocolException("Unexpected content type: " + contentType); @@ -238,7 +239,7 @@ public class DefaultConnection implements Connection { public X509Certificate readCertificate() throws AcmeException { assertConnectionIsOpen(); - String contentType = conn.getHeaderField(CONTENT_TYPE_HEADER); + String contentType = AcmeUtils.getContentType(conn.getHeaderField(CONTENT_TYPE_HEADER)); if (!("application/pkix-cert".equals(contentType))) { throw new AcmeProtocolException("Unexpected content type: " + contentType); } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java index 6e574f32..ee8af251 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java @@ -51,6 +51,9 @@ public final class AcmeUtils { private static final Pattern TZ_PATTERN = Pattern.compile( "([+-])(\\d{2}):?(\\d{2})$"); + private final static Pattern CONTENT_TYPE_PATTERN = Pattern.compile( + "([^;]+)(?:;.*?charset=(\"?)([a-z0-9_-]+)(\\2))?.*", Pattern.CASE_INSENSITIVE); + private AcmeUtils() { // Utility class without constructor } @@ -224,4 +227,27 @@ public final class AcmeUtils { } } + /** + * Extracts the content type of a Content-Type header. + * + * @param header + * Content-Type header + * @return Content-Type, or {@code null} if the header was invalid or empty + * @throws AcmeProtocolException + * if the Content-Type header contains a different charset than "utf-8". + */ + public static String getContentType(String header) { + if (header != null) { + Matcher m = CONTENT_TYPE_PATTERN.matcher(header); + if (m.matches()) { + String charset = m.group(3); + if (charset != null && !"utf-8".equalsIgnoreCase(charset)) { + throw new AcmeProtocolException("Unsupported charset " + charset); + } + return m.group(1).trim().toLowerCase(); + } + } + return null; + } + } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java index 70c338e3..84c25df8 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/AcmeUtilsTest.java @@ -32,6 +32,7 @@ import org.hamcrest.Description; import org.jose4j.jwk.PublicJsonWebKey; import org.junit.BeforeClass; import org.junit.Test; +import org.shredzone.acme4j.exception.AcmeProtocolException; /** * Unit tests for {@link AcmeUtils}. @@ -231,6 +232,35 @@ public class AcmeUtilsTest { assertThat(stripErrorPrefix(null), is(nullValue())); } + /** + * Test {@link AcmeUtils#getContentType(String)}. + */ + @Test + public void testGetContentType() { + assertThat(AcmeUtils.getContentType(null), is(nullValue())); + assertThat(AcmeUtils.getContentType("application/json"), + is("application/json")); + assertThat(AcmeUtils.getContentType("Application/Problem+JSON"), + is("application/problem+json")); + assertThat(AcmeUtils.getContentType("application/json; charset=utf-8"), + is("application/json")); + assertThat(AcmeUtils.getContentType("application/json; charset=utf-8 (Plain text)"), + is("application/json")); + assertThat(AcmeUtils.getContentType("application/json; charset=\"utf-8\""), + is("application/json")); + assertThat(AcmeUtils.getContentType("application/json; charset=\"UTF-8\"; foo=4"), + is("application/json")); + assertThat(AcmeUtils.getContentType(" application/json ;foo=4"), + is("application/json")); + + try { + AcmeUtils.getContentType("application/json; charset=\"iso-8859-1\""); + fail("Accepted bad charset"); + } catch (AcmeProtocolException ex) { + // expected + } + } + /** * Matches the given time. */ From 04a40c83e0971e8ef02472d5b6f1f20eed23e6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 4 Nov 2017 12:49:49 +0100 Subject: [PATCH 26/38] Use URL for locations Fixes issue #40 --- .../org/shredzone/acme4j/AcmeResource.java | 8 +- .../org/shredzone/acme4j/Authorization.java | 6 +- .../org/shredzone/acme4j/Certificate.java | 46 ++--- .../org/shredzone/acme4j/Registration.java | 35 ++-- .../shredzone/acme4j/RegistrationBuilder.java | 7 +- .../java/org/shredzone/acme4j/Session.java | 19 +- .../shredzone/acme4j/challenge/Challenge.java | 10 +- .../acme4j/connector/Connection.java | 31 ++-- .../acme4j/connector/DefaultConnection.java | 111 +++++++----- .../acme4j/connector/HttpConnector.java | 14 +- .../acme4j/connector/ResourceIterator.java | 54 +++--- .../exception/AcmeConflictException.java | 10 +- .../acme4j/provider/AcmeProvider.java | 7 +- .../acme4j/provider/GenericAcmeProvider.java | 10 +- .../letsencrypt/LetsEncryptAcmeProvider.java | 21 +-- .../letsencrypt/LetsEncryptHttpConnector.java | 6 +- .../shredzone/acme4j/AcmeResourceTest.java | 4 +- .../shredzone/acme4j/AuthorizationTest.java | 34 ++-- .../org/shredzone/acme4j/CertificateTest.java | 54 +++--- .../acme4j/RegistrationBuilderTest.java | 19 +- .../shredzone/acme4j/RegistrationTest.java | 165 +++++++++--------- .../org/shredzone/acme4j/SessionTest.java | 30 ++-- .../acme4j/challenge/ChallengeTest.java | 40 ++--- .../connector/DefaultConnectionTest.java | 71 ++------ .../acme4j/connector/DummyConnection.java | 12 +- .../acme4j/connector/HttpConnectorTest.java | 4 +- .../connector/ResourceIteratorTest.java | 44 ++--- .../acme4j/connector/SessionProviderTest.java | 5 +- .../exception/AcmeConflictExceptionTest.java | 9 +- .../provider/AbstractAcmeProviderTest.java | 15 +- .../provider/GenericAcmeProviderTest.java | 8 +- .../provider/TestableConnectionProvider.java | 7 +- .../LetsEncryptAcmeProviderTest.java | 9 +- .../LetsEncryptHttpConnectorTest.java | 6 +- .../shredzone/acme4j/toolbox/TestUtils.java | 18 ++ .../java/org/shredzone/acme4j/ClientTest.java | 10 +- src/site/markdown/migration.md | 4 + src/site/markdown/provider.md | 8 +- src/site/markdown/usage/authorization.md | 12 +- src/site/markdown/usage/certificate.md | 6 +- src/site/markdown/usage/register.md | 6 +- src/site/markdown/usage/session.md | 6 +- 42 files changed, 526 insertions(+), 475 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java index ee2d7fd1..27db3fed 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j; import java.io.Serializable; -import java.net.URI; +import java.net.URL; import java.util.Objects; /** @@ -24,7 +24,7 @@ public abstract class AcmeResource implements Serializable { private static final long serialVersionUID = -7930580802257379731L; private transient Session session; - private URI location; + private URL location; /** * Create a new {@link AcmeResource}. @@ -57,7 +57,7 @@ public abstract class AcmeResource implements Serializable { /** * Sets the resource's location. */ - protected void setLocation(URI location) { + protected void setLocation(URL location) { this.location = Objects.requireNonNull(location, "location"); } @@ -81,7 +81,7 @@ public abstract class AcmeResource implements Serializable { /** * Gets the resource's location. */ - public URI getLocation() { + public URL getLocation() { return location; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java index 0cd7af81..70b7d090 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java @@ -17,7 +17,7 @@ import static java.util.stream.Collectors.toList; import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -49,7 +49,7 @@ public class Authorization extends AcmeResource { private List> combinations; private boolean loaded = false; - protected Authorization(Session session, URI location) { + protected Authorization(Session session, URL location) { super(session); setLocation(location); } @@ -64,7 +64,7 @@ public class Authorization extends AcmeResource { * Location of the Authorization * @return {@link Authorization} bound to the session and location */ - public static Authorization bind(Session session, URI location) { + public static Authorization bind(Session session, URL location) { return new Authorization(session, location); } 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 d7caf6cd..e6314fae 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -14,7 +14,8 @@ package org.shredzone.acme4j; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.MalformedURLException; +import java.net.URL; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -37,19 +38,19 @@ public class Certificate extends AcmeResource { private static final Logger LOG = LoggerFactory.getLogger(Certificate.class); private static final int MAX_CHAIN_LENGTH = 10; - private URI chainCertUri; + private URL chainCertUrl; private X509Certificate cert = null; private X509Certificate[] chain = null; - protected Certificate(Session session, URI certUri) { + protected Certificate(Session session, URL certUrl) { super(session); - setLocation(certUri); + setLocation(certUrl); } - protected Certificate(Session session, URI certUri, URI chainUri, X509Certificate cert) { + protected Certificate(Session session, URL certUrl, URL chainUrl, X509Certificate cert) { super(session); - setLocation(certUri); - this.chainCertUri = chainUri; + setLocation(certUrl); + this.chainCertUrl = chainUrl; this.cert = cert; } @@ -62,16 +63,16 @@ public class Certificate extends AcmeResource { * Location of the Certificate * @return {@link Certificate} bound to the session and location */ - public static Certificate bind(Session session, URI location) { + public static Certificate bind(Session session, URL location) { return new Certificate(session, location); } /** - * Returns the URI of the certificate chain. {@code null} if not known or not + * Returns the URL of the certificate chain. {@code null} if not known or not * available. */ - public URI getChainLocation() { - return chainCertUri; + public URL getChainLocation() { + return chainCertUrl; } /** @@ -92,7 +93,7 @@ public class Certificate extends AcmeResource { conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED); conn.handleRetryAfter("certificate is not available for download yet"); - chainCertUri = conn.getLink("up"); + chainCertUrl = conn.getLink("up"); cert = conn.readCertificate(); } } @@ -111,21 +112,21 @@ public class Certificate extends AcmeResource { */ public X509Certificate[] downloadChain() throws AcmeException { if (chain == null) { - if (chainCertUri == null) { + if (chainCertUrl == null) { download(); } - if (chainCertUri == null) { + if (chainCertUrl == null) { throw new AcmeProtocolException("No certificate chain provided"); } LOG.debug("downloadChain"); List certChain = new ArrayList<>(); - URI link = chainCertUri; + URL link = chainCertUrl; while (link != null && certChain.size() < MAX_CHAIN_LENGTH) { try (Connection conn = getSession().provider().connect()) { - conn.sendRequest(chainCertUri, getSession()); + conn.sendRequest(chainCertUrl, getSession()); conn.accept(HttpURLConnection.HTTP_OK); certChain.add(conn.readCertificate()); @@ -159,8 +160,8 @@ public class Certificate extends AcmeResource { */ public void revoke(RevocationReason reason) throws AcmeException { LOG.debug("revoke"); - URI resUri = getSession().resourceUri(Resource.REVOKE_CERT); - if (resUri == null) { + URL resUrl = getSession().resourceUrl(Resource.REVOKE_CERT); + if (resUrl == null) { throw new AcmeProtocolException("CA does not support certificate revocation"); } @@ -176,7 +177,7 @@ public class Certificate extends AcmeResource { claims.put("reason", reason.getReasonCode()); } - conn.sendSignedRequest(resUri, claims, getSession()); + conn.sendSignedRequest(resUrl, claims, getSession()); conn.accept(HttpURLConnection.HTTP_OK); } catch (CertificateEncodingException ex) { throw new AcmeProtocolException("Invalid certificate", ex); @@ -199,7 +200,12 @@ public class Certificate extends AcmeResource { */ public static void revoke(Session session, X509Certificate cert, RevocationReason reason) throws AcmeException { - new Certificate(session, URI.create(""), null, cert).revoke(reason); + try { + URL dummyUrl = new URL("http://"); + new Certificate(session, dummyUrl, null, cert).revoke(reason); + } catch (MalformedURLException ex) { + throw new InternalError(ex); + } } } 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 2a6b7eea..8cf34401 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java @@ -17,6 +17,7 @@ import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; @@ -56,17 +57,17 @@ public class Registration extends AcmeResource { private final List contacts = new ArrayList<>(); private URI agreement; - private URI authorizations; - private URI certificates; + private URL authorizations; + private URL certificates; private Status status; private boolean loaded = false; - protected Registration(Session session, URI location) { + protected Registration(Session session, URL location) { super(session); setLocation(location); } - protected Registration(Session session, URI location, URI agreement) { + protected Registration(Session session, URL location, URI agreement) { super(session); setLocation(location); this.agreement = agreement; @@ -78,10 +79,10 @@ public class Registration extends AcmeResource { * @param session * {@link Session} to be used * @param location - * Location URI of the registration + * Location URL of the registration * @return {@link Registration} bound to the session and location */ - public static Registration bind(Session session, URI location) { + public static Registration bind(Session session, URL location) { return new Registration(session, location); } @@ -185,7 +186,7 @@ public class Registration extends AcmeResource { .put("type", "dns") .put("value", toAce(domain)); - conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_AUTHZ), claims, getSession()); + conn.sendSignedRequest(getSession().resourceUrl(Resource.NEW_AUTHZ), claims, getSession()); conn.accept(HttpURLConnection.HTTP_CREATED); JSON json = conn.readJsonResponse(); @@ -240,7 +241,7 @@ public class Registration extends AcmeResource { claims.put("notAfter", notAfter); } - conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_CERT), claims, getSession()); + conn.sendSignedRequest(getSession().resourceUrl(Resource.NEW_CERT), claims, getSession()); int rc = conn.accept(HttpURLConnection.HTTP_CREATED, HttpURLConnection.HTTP_ACCEPTED); X509Certificate cert = null; @@ -252,9 +253,9 @@ public class Registration extends AcmeResource { } } - URI chainCertUri = conn.getLink("up"); + URL chainCertUrl = conn.getLink("up"); - return new Certificate(getSession(), conn.getLocation(), chainCertUri, cert); + return new Certificate(getSession(), conn.getLocation(), chainCertUrl, cert); } } @@ -277,7 +278,7 @@ public class Registration extends AcmeResource { LOG.debug("key-change"); try (Connection conn = getSession().provider().connect()) { - URI keyChangeUri = getSession().resourceUri(Resource.KEY_CHANGE); + URL keyChangeUrl = getSession().resourceUrl(Resource.KEY_CHANGE); PublicJsonWebKey newKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(newKeyPair.getPublic()); JSONBuilder payloadClaim = new JSONBuilder(); @@ -286,7 +287,7 @@ public class Registration extends AcmeResource { JsonWebSignature innerJws = new JsonWebSignature(); innerJws.setPayload(payloadClaim.toString()); - innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUri); + innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUrl); innerJws.getHeaders().setJwkHeaderValue("jwk", newKeyJwk); innerJws.setAlgorithmHeaderValue(keyAlgorithm(newKeyJwk)); innerJws.setKey(newKeyPair.getPrivate()); @@ -298,7 +299,7 @@ public class Registration extends AcmeResource { outerClaim.put("signature", innerJws.getEncodedSignature()); outerClaim.put("payload", innerJws.getEncodedPayload()); - conn.sendSignedRequest(keyChangeUri, outerClaim, getSession()); + conn.sendSignedRequest(keyChangeUrl, outerClaim, getSession()); conn.accept(HttpURLConnection.HTTP_OK); getSession().setKeyPair(newKeyPair); @@ -361,19 +362,19 @@ public class Registration extends AcmeResource { .forEach(contacts::add); } - this.authorizations = json.get(KEY_AUTHORIZATIONS).asURI(); - this.certificates = json.get(KEY_CERTIFICATES).asURI(); + this.authorizations = json.get(KEY_AUTHORIZATIONS).asURL(); + this.certificates = json.get(KEY_CERTIFICATES).asURL(); if (json.contains(KEY_STATUS)) { this.status = Status.parse(json.get(KEY_STATUS).asString()); } - URI location = conn.getLocation(); + URL location = conn.getLocation(); if (location != null) { setLocation(location); } - URI tos = conn.getLink("terms-of-service"); + URI tos = conn.getLinkAsURI("terms-of-service"); if (tos != null) { this.agreement = tos; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java index 93dceff4..9c8390f8 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -83,11 +84,11 @@ public class RegistrationBuilder { claims.put("contact", contacts); } - conn.sendSignedRequest(session.resourceUri(Resource.NEW_REG), claims, session); + conn.sendSignedRequest(session.resourceUrl(Resource.NEW_REG), claims, session); conn.accept(HttpURLConnection.HTTP_CREATED); - URI location = conn.getLocation(); - URI tos = conn.getLink("terms-of-service"); + URL location = conn.getLocation(); + URI tos = conn.getLinkAsURI("terms-of-service"); return new Registration(session, location, tos); } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java index 2984be37..f7a8bd0c 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java @@ -14,6 +14,7 @@ package org.shredzone.acme4j; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.time.Duration; import java.time.Instant; @@ -40,7 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON; * volatile data. */ public class Session { - private final AtomicReference> resourceMap = new AtomicReference<>(); + private final AtomicReference> resourceMap = new AtomicReference<>(); private final AtomicReference metadata = new AtomicReference<>(); private final URI serverUri; private final AcmeProvider provider; @@ -175,14 +176,14 @@ public class Session { } /** - * Gets the {@link URI} of the given {@link Resource}. This may involve connecting to + * Gets the {@link URL} of the given {@link Resource}. This may involve connecting to * the server and getting a directory. The result is cached. * * @param resource - * {@link Resource} to get the {@link URI} of - * @return {@link URI}, or {@code null} if the server does not offer that resource + * {@link Resource} to get the {@link URL} of + * @return {@link URL}, or {@code null} if the server does not offer that resource */ - public URI resourceUri(Resource resource) throws AcmeException { + public URL resourceUrl(Resource resource) throws AcmeException { readDirectory(); return resourceMap.get().get(Objects.requireNonNull(resource, "resource")); } @@ -219,11 +220,11 @@ public class Session { metadata.set(new Metadata(JSON.empty())); } - Map map = new EnumMap<>(Resource.class); + Map map = new EnumMap<>(Resource.class); for (Resource res : Resource.values()) { - URI uri = directoryJson.get(res.path()).asURI(); - if (uri != null) { - map.put(res, uri); + URL url = directoryJson.get(res.path()).asURL(); + if (url != null) { + map.put(res, url); } } resourceMap.set(map); diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java index 8534174c..85150d43 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j.challenge; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Instant; import java.util.Objects; @@ -72,7 +72,7 @@ public class Challenge extends AcmeResource { * @return {@link Challenge} bound to this session and location */ @SuppressWarnings("unchecked") - public static T bind(Session session, URI location) throws AcmeException { + public static T bind(Session session, URL location) throws AcmeException { Objects.requireNonNull(session, "session"); Objects.requireNonNull(location, "location"); @@ -105,11 +105,11 @@ public class Challenge extends AcmeResource { } /** - * Returns the location {@link URI} of the challenge. + * Returns the location {@link URL} of the challenge. */ @Override - public URI getLocation() { - return data.get(KEY_URI).asURI(); + public URL getLocation() { + return data.get(KEY_URI).asURL(); } /** diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java index c1240675..3c67f3dd 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java @@ -14,8 +14,8 @@ package org.shredzone.acme4j.connector; import java.net.URI; +import java.net.URL; import java.security.cert.X509Certificate; -import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; @@ -31,24 +31,24 @@ public interface Connection extends AutoCloseable { /** * Sends a simple GET request. * - * @param uri - * {@link URI} to send the request to. + * @param url + * {@link URL} to send the request to. * @param session * {@link Session} instance to be used for tracking */ - void sendRequest(URI uri, Session session) throws AcmeException; + void sendRequest(URL url, Session session) throws AcmeException; /** * Sends a signed POST request. * - * @param uri - * {@link URI} to send the request to. + * @param url + * {@link URL} to send the request to. * @param claims * {@link JSONBuilder} containing claims. Must not be {@code null}. * @param session * {@link Session} instance to be used for signing and tracking */ - void sendSignedRequest(URI uri, JSONBuilder claims, Session session) throws AcmeException; + void sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException; /** * Checks if the HTTP response status is in the given list of acceptable HTTP states, @@ -96,9 +96,9 @@ public interface Connection extends AutoCloseable { *

* Relative links are resolved against the last request's URL. * - * @return Location {@link URI}, or {@code null} if no Location header was set + * @return Location {@link URL}, or {@code null} if no Location header was set */ - URI getLocation(); + URL getLocation(); /** * Gets a relation link from the header. @@ -108,20 +108,21 @@ public interface Connection extends AutoCloseable { * * @param relation * Link relation - * @return Link, or {@code null} if there was no such relation link + * @return Link {@link URL}, or {@code null} if there was no such relation link */ - URI getLink(String relation); + URL getLink(String relation); /** - * Gets one or more relation link from the header. + * Gets a relation link from the header. *

- * Relative links are resolved against the last request's URL. + * Relative links are resolved against the last request's URL. If there is more than + * one relation, the first one is returned. * * @param relation * Link relation - * @return Collection of links, or {@code null} if there was no such relation link + * @return Link {@link URI}, or {@code null} if there was no such relation link */ - Collection getLinks(String relation); + URI getLinkAsURI(String relation); /** * Closes the {@link Connection}, releasing all resources. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 245893d4..da3b05ec 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -13,6 +13,7 @@ */ package org.shredzone.acme4j.connector; +import static java.util.stream.Collectors.toList; import static org.shredzone.acme4j.toolbox.AcmeUtils.keyAlgorithm; import java.io.IOException; @@ -91,15 +92,15 @@ public class DefaultConnection implements Connection { } @Override - public void sendRequest(URI uri, Session session) throws AcmeException { - Objects.requireNonNull(uri, "uri"); + public void sendRequest(URL url, Session session) throws AcmeException { + Objects.requireNonNull(url, "url"); Objects.requireNonNull(session, "session"); assertConnectionIsClosed(); - LOG.debug("GET {}", uri); + LOG.debug("GET {}", url); try { - conn = httpConnector.openConnection(uri); + conn = httpConnector.openConnection(url); conn.setRequestMethod("GET"); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); @@ -114,8 +115,8 @@ public class DefaultConnection implements Connection { } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) throws AcmeException { - Objects.requireNonNull(uri, "uri"); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException { + Objects.requireNonNull(url, "url"); Objects.requireNonNull(claims, "claims"); Objects.requireNonNull(session, "session"); assertConnectionIsClosed(); @@ -124,8 +125,8 @@ public class DefaultConnection implements Connection { KeyPair keypair = session.getKeyPair(); if (session.getNonce() == null) { - LOG.debug("Getting initial nonce, HEAD {}", uri); - conn = httpConnector.openConnection(uri); + LOG.debug("Getting initial nonce, HEAD {}", url); + conn = httpConnector.openConnection(url); conn.setRequestMethod("HEAD"); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); conn.connect(); @@ -137,9 +138,9 @@ public class DefaultConnection implements Connection { throw new AcmeProtocolException("Server did not provide a nonce"); } - LOG.debug("POST {} with claims: {}", uri, claims); + LOG.debug("POST {} with claims: {}", url, claims); - conn = httpConnector.openConnection(uri); + conn = httpConnector.openConnection(url); conn.setRequestMethod("POST"); conn.setRequestProperty(ACCEPT_HEADER, "application/json"); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); @@ -152,7 +153,7 @@ public class DefaultConnection implements Connection { JsonWebSignature jws = new JsonWebSignature(); jws.setPayload(claims.toString()); jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce())); - jws.getHeaders().setObjectHeaderValue("url", uri); + jws.getHeaders().setObjectHeaderValue("url", url); jws.getHeaders().setJwkHeaderValue("jwk", jwk); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setKey(keypair.getPrivate()); @@ -289,7 +290,7 @@ public class DefaultConnection implements Connection { } @Override - public URI getLocation() { + public URL getLocation() { assertConnectionIsOpen(); String location = conn.getHeaderField(LOCATION_HEADER); @@ -302,24 +303,37 @@ public class DefaultConnection implements Connection { } @Override - public URI getLink(String relation) { - Collection links = getLinks(relation); - if (links == null) { - return null; - } - - if (links.size() > 1) { - LOG.debug("Link: {} - using the first of {}", relation, links.size()); - } - - return links.iterator().next(); + public URL getLink(String relation) { + return getLinks(relation).stream() + .findFirst() + .map(this::resolveRelative) + .orElse(null); } @Override - public Collection getLinks(String relation) { + public URI getLinkAsURI(String relation) { + return getLinks(relation).stream() + .findFirst() + .map(this::resolveRelativeAsURI) + .orElse(null); + } + + @Override + public void close() { + conn = null; + } + + /** + * Returns the link headers of the given relation. The link URIs are unresolved. + * + * @param relation + * Relation name + * @return Link headers + */ + private Collection getLinks(String relation) { assertConnectionIsOpen(); - List result = new ArrayList<>(); + List result = new ArrayList<>(); List links = conn.getHeaderFields().get(LINK_HEADER); if (links != null) { @@ -329,17 +343,12 @@ public class DefaultConnection implements Connection { if (m.matches()) { String location = m.group(1); LOG.debug("Link: {} -> {}", relation, location); - result.add(resolveRelative(location)); + result.add(location); } } } - return !result.isEmpty() ? result : null; - } - - @Override - public void close() { - conn = null; + return result; } /** @@ -389,14 +398,16 @@ public class DefaultConnection implements Connection { } if ("agreementRequired".equals(error)) { - URI instance = resolveRelative(json.get("instance").asString()); - URI tos = getLink("terms-of-service"); + URI instance = resolveRelativeAsURI(json.get("instance").asString()); + URI tos = getLinkAsURI("terms-of-service"); return new AcmeAgreementRequiredException(type, detail, tos, instance); } if ("rateLimited".equals(error)) { Optional retryAfter = getRetryAfterHeader(); - Collection rateLimits = getLinks("rate-limit"); + Collection rateLimits = getLinks("rate-limit").stream() + .map(this::resolveRelativeAsURI) + .collect(toList()); return new AcmeRateLimitExceededException(type, detail, retryAfter.orElse(null), rateLimits); } @@ -437,7 +448,29 @@ public class DefaultConnection implements Connection { } /** - * Resolves a relative link against the connection's last URI. + * Resolves a relative link against the connection's last URL. + * + * @param link + * Link to resolve. Absolute links are just converted to an URL. May be + * {@code null}. + * @return Absolute URL of the given link, or {@code null} if the link was + * {@code null}. + */ + private URL resolveRelative(String link) { + if (link == null) { + return null; + } + + assertConnectionIsOpen(); + try { + return new URL(conn.getURL(), link); + } catch (MalformedURLException ex) { + throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex); + } + } + + /** + * Resolves a relative link against the connection's last URL. * * @param link * Link to resolve. Absolute links are just converted to an URI. May be @@ -445,15 +478,15 @@ public class DefaultConnection implements Connection { * @return Absolute URI of the given link, or {@code null} if the link was * {@code null}. */ - private URI resolveRelative(String link) { + private URI resolveRelativeAsURI(String link) { if (link == null) { return null; } assertConnectionIsOpen(); try { - return new URL(conn.getURL(), link).toURI(); - } catch (MalformedURLException | URISyntaxException ex) { + return conn.getURL().toURI().resolve(link); + } catch (URISyntaxException ex) { throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java index 5e543176..ecfe6fea 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j.connector; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.Properties; import org.slf4j.LoggerFactory; @@ -59,14 +59,14 @@ public class HttpConnector { } /** - * Opens a {@link HttpURLConnection} to the given {@link URI}. + * Opens a {@link HttpURLConnection} to the given {@link URL}. * - * @param uri - * {@link URI} to connect to - * @return {@link HttpURLConnection} connected to the {@link URI} + * @param url + * {@link URL} to connect to + * @return {@link HttpURLConnection} connected to the {@link URL} */ - public HttpURLConnection openConnection(URI uri) throws IOException { - HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); + public HttpURLConnection openConnection(URL url) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); configure(conn); return conn; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java index 71f37f54..0799d3d3 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j.connector; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; @@ -29,7 +29,7 @@ import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.toolbox.JSON; /** - * An {@link Iterator} that fetches a batch of URIs from the ACME server, and generates + * An {@link Iterator} that fetches a batch of URLs from the ACME server, and generates * {@link AcmeResource} instances. * * @param @@ -39,10 +39,10 @@ public class ResourceIterator implements Iterator { private final Session session; private final String field; - private final Deque uriList = new ArrayDeque<>(); - private final BiFunction creator; + private final Deque urlList = new ArrayDeque<>(); + private final BiFunction creator; private boolean eol = false; - private URI nextUri; + private URL nextUrl; /** * Creates a new {@link ResourceIterator}. @@ -52,15 +52,15 @@ public class ResourceIterator implements Iterator { * @param field * Field name to be used in the JSON response * @param start - * URI of the first JSON array, may be {@code null} for an empty iterator + * URL of the first JSON array, may be {@code null} for an empty iterator * @param creator * Creator for an {@link AcmeResource} that is bound to the given - * {@link Session} and {@link URI}. + * {@link Session} and {@link URL}. */ - public ResourceIterator(Session session, String field, URI start, BiFunction creator) { + public ResourceIterator(Session session, String field, URL start, BiFunction creator) { this.session = Objects.requireNonNull(session, "session"); this.field = Objects.requireNonNull(field, "field"); - this.nextUri = start; + this.nextUrl = start; this.creator = Objects.requireNonNull(creator, "creator"); } @@ -68,7 +68,7 @@ public class ResourceIterator implements Iterator { * Checks if there is another object in the result. * * @throws AcmeProtocolException - * if the next batch of URIs could not be fetched from the server + * if the next batch of URLs could not be fetched from the server */ @Override public boolean hasNext() { @@ -76,32 +76,32 @@ public class ResourceIterator implements Iterator { return false; } - if (uriList.isEmpty()) { + if (urlList.isEmpty()) { fetch(); } - if (uriList.isEmpty()) { + if (urlList.isEmpty()) { eol = true; } - return !uriList.isEmpty(); + return !urlList.isEmpty(); } /** * Returns the next object of the result. * * @throws AcmeProtocolException - * if the next batch of URIs could not be fetched from the server + * if the next batch of URLs could not be fetched from the server * @throws NoSuchElementException * if there are no more entries */ @Override public T next() { - if (!eol && uriList.isEmpty()) { + if (!eol && urlList.isEmpty()) { fetch(); } - URI next = uriList.poll(); + URL next = urlList.poll(); if (next == null) { eol = true; throw new NoSuchElementException("no more " + field); @@ -119,11 +119,11 @@ public class ResourceIterator implements Iterator { } /** - * Fetches the next batch of URIs. Handles exceptions. Does nothing if there is no - * URI of the next batch. + * Fetches the next batch of URLs. Handles exceptions. Does nothing if there is no + * URL of the next batch. */ private void fetch() { - if (nextUri == null) { + if (nextUrl == null) { return; } @@ -135,34 +135,34 @@ public class ResourceIterator implements Iterator { } /** - * Reads the next batch of URIs from the server, and fills the queue with the URIs. If - * there is a "next" header, it is used for the next batch of URIs. + * Reads the next batch of URLs from the server, and fills the queue with the URLs. If + * there is a "next" header, it is used for the next batch of URLs. */ private void readAndQueue() throws AcmeException { try (Connection conn = session.provider().connect()) { - conn.sendRequest(nextUri, session); + conn.sendRequest(nextUrl, session); conn.accept(HttpURLConnection.HTTP_OK); JSON json = conn.readJsonResponse(); - fillUriList(json); + fillUrlList(json); - nextUri = conn.getLink("next"); + nextUrl = conn.getLink("next"); } } /** - * Fills the uri list with the URIs found in the desired field. + * Fills the url list with the URLs found in the desired field. * * @param json * JSON map to read from */ - private void fillUriList(JSON json) { + private void fillUrlList(JSON json) { JSON.Array array = json.get(field).asArray(); if (array == null) { return; } - array.stream().map(JSON.Value::asURI).forEach(uriList::add); + array.stream().map(JSON.Value::asURL).forEach(urlList::add); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java index 7926bead..b276441d 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.exception; -import java.net.URI; +import java.net.URL; import java.util.Objects; /** @@ -23,7 +23,7 @@ import java.util.Objects; public class AcmeConflictException extends AcmeException { private static final long serialVersionUID = 7454201988845449591L; - private final URI location; + private final URL location; /** * Creates a new {@link AcmeConflictException}. @@ -31,9 +31,9 @@ public class AcmeConflictException extends AcmeException { * @param msg * Details about the conflicting resource * @param location - * {@link URI} of the conflicting resource + * {@link URL} of the conflicting resource */ - public AcmeConflictException(String msg, URI location) { + public AcmeConflictException(String msg, URL location) { super(msg); this.location = Objects.requireNonNull(location, "location"); } @@ -41,7 +41,7 @@ public class AcmeConflictException extends AcmeException { /** * Location of the conflicting resource. */ - public URI getLocation() { + public URL getLocation() { return location; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java index c563ad2a..8b8be1e6 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java @@ -14,6 +14,7 @@ package org.shredzone.acme4j.provider; import java.net.URI; +import java.net.URL; import java.util.ServiceLoader; import org.shredzone.acme4j.Session; @@ -41,15 +42,15 @@ public interface AcmeProvider { boolean accepts(URI serverUri); /** - * Resolves the server URI and returns the matching directory URI. + * Resolves the server URI and returns the matching directory URL. * * @param serverUri * Server {@link URI} - * @return Resolved directory {@link URI} + * @return Resolved directory {@link URL} * @throws IllegalArgumentException * if the server {@link URI} is not accepted */ - URI resolve(URI serverUri); + URL resolve(URI serverUri); /** * Creates a {@link Connection} for communication with the ACME server. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java index 0ce2990d..5ab59c73 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java @@ -13,7 +13,9 @@ */ package org.shredzone.acme4j.provider; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; /** * A generic {@link AcmeProvider}. It should be working for all ACME servers complying to @@ -30,8 +32,12 @@ public class GenericAcmeProvider extends AbstractAcmeProvider { } @Override - public URI resolve(URI serverUri) { - return serverUri; + public URL resolve(URI serverUri) { + try { + return serverUri.toURL(); + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("Invalid server URI", ex); + } } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java index d2dee002..e75f7746 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java @@ -13,8 +13,9 @@ */ package org.shredzone.acme4j.provider.letsencrypt; +import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; +import java.net.URL; import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.exception.AcmeProtocolException; @@ -37,8 +38,8 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { private static final Logger LOG = LoggerFactory.getLogger(LetsEncryptAcmeProvider.class); - private static final String V01_DIRECTORY_URI = "https://acme-v01.api.letsencrypt.org/directory"; - private static final String STAGING_DIRECTORY_URI = "https://acme-staging.api.letsencrypt.org/directory"; + private static final String V01_DIRECTORY_URL = "https://acme-v01.api.letsencrypt.org/directory"; + private static final String STAGING_DIRECTORY_URL = "https://acme-staging.api.letsencrypt.org/directory"; @Override public boolean accepts(URI serverUri) { @@ -47,21 +48,21 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { String path = serverUri.getPath(); - String directoryUri; + String directoryUrl; if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) { - directoryUri = V01_DIRECTORY_URI; + directoryUrl = V01_DIRECTORY_URL; } else if ("/staging".equals(path)) { - directoryUri = STAGING_DIRECTORY_URI; + directoryUrl = STAGING_DIRECTORY_URL; } else { throw new IllegalArgumentException("Unknown URI " + serverUri); } try { - return new URI(directoryUri); - } catch (URISyntaxException ex) { - throw new AcmeProtocolException(directoryUri, ex); + return new URL(directoryUrl); + } catch (MalformedURLException ex) { + throw new AcmeProtocolException(directoryUrl, ex); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java index 1f719561..72085727 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -42,8 +42,8 @@ public class LetsEncryptHttpConnector extends HttpConnector { private static SSLSocketFactory sslSocketFactory; @Override - public HttpURLConnection openConnection(URI uri) throws IOException { - HttpURLConnection conn = super.openConnection(uri); + public HttpURLConnection openConnection(URL url) throws IOException { + HttpURLConnection conn = super.openConnection(url); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory()); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java index 27c32987..16497629 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java @@ -20,7 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.net.URI; +import java.net.URL; import org.junit.Test; import org.shredzone.acme4j.toolbox.TestUtils; @@ -36,7 +36,7 @@ public class AcmeResourceTest { @Test public void testConstructor() throws Exception { Session session = TestUtils.session(); - URI location = new URI("http://example.com/acme/resource"); + URL location = new URL("http://example.com/acme/resource"); try { new DummyResource(null); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index 182b7597..9dcbcdfa 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -20,7 +20,7 @@ import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Duration; import java.time.Instant; import java.util.Collection; @@ -44,7 +44,7 @@ public class AuthorizationTest { private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge - private URI locationUri = URI.create("http://example.com/acme/registration");; + private URL locationUrl = url("http://example.com/acme/registration"); /** * Test that {@link Authorization#findChallenge(String)} does only find standalone @@ -121,8 +121,8 @@ public class AuthorizationTest { public void testUpdate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -150,13 +150,13 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("dns-01", dnsChallenge); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); auth.update(); assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -179,9 +179,9 @@ public class AuthorizationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { requestWasSent.set(true); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); } @Override @@ -207,7 +207,7 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", new Http01Challenge(session)); provider.putTestChallenge("dns-01", new Dns01Challenge(session)); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); // Lazy loading assertThat(requestWasSent.get(), is(false)); @@ -233,8 +233,8 @@ public class AuthorizationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -262,7 +262,7 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("dns-01", dnsChallenge); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); try { auth.update(); @@ -274,7 +274,7 @@ public class AuthorizationTest { assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -295,11 +295,11 @@ public class AuthorizationTest { public void testDeactivate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { JSON json = claims.toJSON(); assertThat(json.get("resource").asString(), is("authz")); assertThat(json.get("status").asString(), is("deactivated")); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); } @@ -311,7 +311,7 @@ public class AuthorizationTest { } }; - Authorization auth = new Authorization(provider.createSession(), locationUri); + Authorization auth = new Authorization(provider.createSession(), locationUrl); auth.deactivate(); provider.close(); @@ -328,7 +328,7 @@ public class AuthorizationTest { provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session)); provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session)); - Authorization authorization = new Authorization(session, locationUri); + Authorization authorization = new Authorization(session, locationUrl); authorization.unmarshalAuthorization(getJsonAsObject("authorizationChallenges")); return authorization; } 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 7d391f1f..92954836 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java @@ -20,7 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Duration; @@ -39,9 +39,9 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class CertificateTest { - private URI resourceUri = URI.create("http://example.com/acme/resource"); - private URI locationUri = URI.create("http://example.com/acme/certificate"); - private URI chainUri = URI.create("http://example.com/acme/chain"); + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/certificate"); + private URL chainUrl = url("http://example.com/acme/chain"); /** * Test that a certificate can be downloaded. @@ -51,17 +51,17 @@ public class CertificateTest { final X509Certificate originalCert = TestUtils.createCertificate(); TestableConnectionProvider provider = new TestableConnectionProvider() { - private boolean isLocationUri; + private boolean isLocationUrl; @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, isOneOf(locationUri, chainUri)); - isLocationUri = uri.equals(locationUri); + public void sendRequest(URL url, Session session) { + assertThat(url, isOneOf(locationUrl, chainUrl)); + isLocationUrl = url.equals(locationUrl); } @Override public int accept(int... httpStatus) throws AcmeException { - if (isLocationUri) { + if (isLocationUrl) { // The leaf certificate, might be asynchronous assertThat(httpStatus, isIntArrayContainingInAnyOrder( HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED)); @@ -84,18 +84,18 @@ public class CertificateTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return (isLocationUri ? chainUri : null); + case "up": return (isLocationUrl ? chainUrl : null); default: return null; } } }; - Certificate cert = new Certificate(provider.createSession(), locationUri); + Certificate cert = new Certificate(provider.createSession(), locationUrl); X509Certificate downloadedCert = cert.download(); assertThat(downloadedCert, is(sameInstance(originalCert))); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getChainLocation(), is(chainUrl)); X509Certificate[] downloadedChain = cert.downloadChain(); assertThat(downloadedChain.length, is(1)); @@ -113,8 +113,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -131,7 +131,7 @@ public class CertificateTest { } }; - Certificate cert = new Certificate(provider.createSession(), locationUri); + Certificate cert = new Certificate(provider.createSession(), locationUrl); try { cert.download(); @@ -152,8 +152,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateRequest"))); assertThat(session, is(notNullValue())); } @@ -165,9 +165,9 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); - Certificate cert = new Certificate(provider.createSession(), locationUri, null, originalCert); + Certificate cert = new Certificate(provider.createSession(), locationUrl, null, originalCert); cert.revoke(); provider.close(); @@ -182,8 +182,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(session, is(notNullValue())); } @@ -195,9 +195,9 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); - Certificate cert = new Certificate(provider.createSession(), locationUri, null, originalCert); + Certificate cert = new Certificate(provider.createSession(), locationUrl, null, originalCert); cert.revoke(RevocationReason.KEY_COMPROMISE); provider.close(); @@ -213,8 +213,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(session, is(notNullValue())); assertThat(session.getKeyPair(), is(certKeyPair)); @@ -227,7 +227,7 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); Session session = provider.createSession(); session.setKeyPair(certKeyPair); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java index e60fbb0c..10a66719 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java @@ -20,6 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import org.junit.Test; import org.shredzone.acme4j.connector.Resource; @@ -32,8 +33,8 @@ import org.shredzone.acme4j.toolbox.JSONBuilder; */ public class RegistrationBuilderTest { - private URI resourceUri = URI.create("http://example.com/acme/resource");; - private URI locationUri = URI.create("http://example.com/acme/registration");; + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/registration"); private URI agreementUri = URI.create("http://example.com/agreement.pdf");; /** @@ -43,8 +44,8 @@ public class RegistrationBuilderTest { public void testRegistration() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("newRegistration"))); assertThat(session, is(notNullValue())); } @@ -56,12 +57,12 @@ public class RegistrationBuilderTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -69,14 +70,14 @@ public class RegistrationBuilderTest { } }; - provider.putTestResource(Resource.NEW_REG, resourceUri); + provider.putTestResource(Resource.NEW_REG, resourceUrl); RegistrationBuilder builder = new RegistrationBuilder(); builder.addContact("mailto:foo@example.com"); Registration registration = builder.create(provider.createSession()); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); provider.close(); 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 5663218c..06366fe3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.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.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; @@ -51,10 +52,10 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class RegistrationTest { - private URI resourceUri = URI.create("http://example.com/acme/resource"); - private URI locationUri = URI.create("http://example.com/acme/registration"); + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/registration"); + private URL chainUrl = url("http://example.com/acme/chain"); private URI agreementUri = URI.create("http://example.com/agreement.pdf"); - private URI chainUri = URI.create("http://example.com/acme/chain"); /** * Test that a registration can be updated. @@ -66,8 +67,8 @@ public class RegistrationTest { private Integer response; @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(locationUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(locationUrl)); assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration"))); assertThat(session, is(notNullValue())); jsonResponse = getJsonAsObject("updateRegistrationResponse"); @@ -75,8 +76,8 @@ public class RegistrationTest { } @Override - public void sendRequest(URI uri, Session session) { - if (URI.create("https://example.com/acme/reg/1/authz").equals(uri)) { + public void sendRequest(URL url, Session session) { + if (url("https://example.com/acme/reg/1/authz").equals(url)) { jsonResponse = new JSONBuilder() .array("authorizations", "https://example.com/acme/auth/1") .toJSON(); @@ -84,7 +85,7 @@ public class RegistrationTest { return; } - if (URI.create("https://example.com/acme/reg/1/cert").equals(uri)) { + if (url("https://example.com/acme/reg/1/cert").equals(url)) { jsonResponse = new JSONBuilder() .array("certificates", "https://example.com/acme/cert/1") .toJSON(); @@ -107,24 +108,28 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { + return null; + } + + @Override + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; - case "next": return null; default: return null; } } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); registration.update(); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getContacts(), hasSize(1)); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); @@ -132,14 +137,12 @@ public class RegistrationTest { Iterator authIt = registration.getAuthorizations(); assertThat(authIt, not(nullValue())); - assertThat(authIt.next().getLocation(), - is(URI.create("https://example.com/acme/auth/1"))); + assertThat(authIt.next().getLocation(), is(url("https://example.com/acme/auth/1"))); assertThat(authIt.hasNext(), is(false)); Iterator certIt = registration.getCertificates(); assertThat(certIt, not(nullValue())); - assertThat(certIt.next().getLocation(), - is(URI.create("https://example.com/acme/cert/1"))); + assertThat(certIt.next().getLocation(), is(url("https://example.com/acme/cert/1"))); assertThat(certIt.hasNext(), is(false)); provider.close(); @@ -154,9 +157,9 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { requestWasSent.set(true); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); } @Override @@ -172,12 +175,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -185,7 +188,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); // Lazy loading assertThat(requestWasSent.get(), is(false)); @@ -208,8 +211,8 @@ public class RegistrationTest { public void testAuthorizeDomain() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("newAuthorizationRequest"))); assertThat(session, is(notNullValue())); } @@ -226,8 +229,8 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; @@ -236,19 +239,19 @@ public class RegistrationTest { Http01Challenge httpChallenge = new Http01Challenge(session); Dns01Challenge dnsChallenge = new Dns01Challenge(session); - provider.putTestResource(Resource.NEW_AUTHZ, resourceUri); + provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl); provider.putTestChallenge(Http01Challenge.TYPE, httpChallenge); provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge); String domainName = "example.org"; - Registration registration = new Registration(session, locationUri); + Registration registration = new Registration(session, locationUrl); Authorization auth = registration.authorizeDomain(domainName); assertThat(auth.getDomain(), is(domainName)); assertThat(auth.getStatus(), is(Status.PENDING)); assertThat(auth.getExpires(), is(nullValue())); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -269,7 +272,7 @@ public class RegistrationTest { public void testAuthorizeBadDomain() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider(); Session session = provider.createSession(); - Registration registration = Registration.bind(session, locationUri); + Registration registration = Registration.bind(session, locationUrl); try { registration.authorizeDomain(null); @@ -297,13 +300,13 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { fail("Attempted to download the certificate. Should be downloaded already!"); } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(session, is(notNullValue())); } @@ -321,32 +324,32 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); ZoneId utc = ZoneId.of("UTC"); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant(); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter); assertThat(cert.download(), is(originalCert)); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -358,8 +361,8 @@ public class RegistrationTest { public void testRequestCertificateAsync() throws AcmeException, IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest"))); assertThat(session, is(notNullValue())); } @@ -372,28 +375,28 @@ public class RegistrationTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -406,8 +409,8 @@ public class RegistrationTest { public void testRequestCertificateBrokenSync() throws AcmeException, IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(session, is(notNullValue())); } @@ -425,31 +428,31 @@ public class RegistrationTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); ZoneId utc = ZoneId.of("UTC"); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant(); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -464,9 +467,9 @@ public class RegistrationTest { final TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder payload, Session session) { + public void sendSignedRequest(URL url, JSONBuilder payload, Session session) { try { - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); @@ -487,7 +490,7 @@ public class RegistrationTest { StringBuilder expectedPayload = new StringBuilder(); expectedPayload.append('{'); - expectedPayload.append("\"account\":\"").append(resourceUri).append("\","); + expectedPayload.append("\"account\":\"").append(resourceUrl).append("\","); expectedPayload.append("\"newKey\":{"); expectedPayload.append("\"kty\":\"").append(TestUtils.D_KTY).append("\","); expectedPayload.append("\"e\":\"").append(TestUtils.D_E).append("\","); @@ -506,12 +509,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return resourceUri; + public URL getLocation() { + return resourceUrl; } }; - provider.putTestResource(Resource.KEY_CHANGE, locationUri); + provider.putTestResource(Resource.KEY_CHANGE, locationUrl); Session session = new Session(new URI(TestUtils.ACME_SERVER_URI), oldKeyPair) { @Override @@ -522,7 +525,7 @@ public class RegistrationTest { assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); - Registration registration = new Registration(session, resourceUri); + Registration registration = new Registration(session, resourceUrl); registration.changeKey(newKeyPair); assertThat(session.getKeyPair(), is(sameInstance(newKeyPair))); @@ -536,7 +539,7 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider(); Session session = provider.createSession(); - Registration registration = new Registration(session, locationUri); + Registration registration = new Registration(session, locationUrl); registration.changeKey(session.getKeyPair()); provider.close(); @@ -549,11 +552,11 @@ public class RegistrationTest { public void testDeactivate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { JSON json = claims.toJSON(); assertThat(json.get("resource").asString(), is("reg")); assertThat(json.get("status").asString(), is("deactivated")); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); } @@ -565,7 +568,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); registration.deactivate(); provider.close(); @@ -580,8 +583,8 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(locationUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(locationUrl)); assertThat(claims.toString(), sameJSONAs(getJson("modifyRegistration"))); assertThat(session, is(notNullValue())); } @@ -598,12 +601,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -611,7 +614,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); EditableRegistration editable = registration.modify(); assertThat(editable, notNullValue()); @@ -621,7 +624,7 @@ public class RegistrationTest { editable.getContacts().add(URI.create("mailto:foo3@example.com")); editable.commit(); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getContacts().size(), is(2)); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java index 02591044..5b16793c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.URI; @@ -203,13 +203,13 @@ public class SessionTest { }; }; - assertThat(session.resourceUri(Resource.NEW_REG), - is(URI.create("https://example.com/acme/new-reg"))); - assertThat(session.resourceUri(Resource.NEW_AUTHZ), - is(URI.create("https://example.com/acme/new-authz"))); - assertThat(session.resourceUri(Resource.NEW_CERT), - is(URI.create("https://example.com/acme/new-cert"))); - assertThat(session.resourceUri(Resource.REVOKE_CERT), + assertThat(session.resourceUrl(Resource.NEW_REG), + is(url("https://example.com/acme/new-reg"))); + assertThat(session.resourceUrl(Resource.NEW_AUTHZ), + is(url("https://example.com/acme/new-authz"))); + assertThat(session.resourceUrl(Resource.NEW_CERT), + is(url("https://example.com/acme/new-cert"))); + assertThat(session.resourceUrl(Resource.REVOKE_CERT), is(nullValue())); Metadata meta = session.getMetadata(); @@ -227,13 +227,13 @@ public class SessionTest { * {@link Session} to assert */ private void assertSession(Session session) throws AcmeException { - assertThat(session.resourceUri(Resource.NEW_REG), - is(URI.create("https://example.com/acme/new-reg"))); - assertThat(session.resourceUri(Resource.NEW_AUTHZ), - is(URI.create("https://example.com/acme/new-authz"))); - assertThat(session.resourceUri(Resource.NEW_CERT), - is(URI.create("https://example.com/acme/new-cert"))); - assertThat(session.resourceUri(Resource.REVOKE_CERT), + assertThat(session.resourceUrl(Resource.NEW_REG), + is(url("https://example.com/acme/new-reg"))); + assertThat(session.resourceUrl(Resource.NEW_AUTHZ), + is(url("https://example.com/acme/new-authz"))); + assertThat(session.resourceUrl(Resource.NEW_CERT), + is(url("https://example.com/acme/new-cert"))); + assertThat(session.resourceUrl(Resource.REVOKE_CERT), is(nullValue())); Metadata meta = session.getMetadata(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java index 5c6ec946..bdcd9c9a 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java @@ -46,8 +46,8 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class ChallengeTest { private Session session; - private URI resourceUri = URI.create("https://example.com/acme/some-resource"); - private URI locationUri = URI.create("https://example.com/acme/some-location"); + private URL resourceUrl = url("https://example.com/acme/some-resource"); + private URL locationUrl = url("https://example.com/acme/some-location"); @Before public void setup() throws IOException { @@ -61,8 +61,8 @@ public class ChallengeTest { public void testChallenge() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -82,11 +82,11 @@ public class ChallengeTest { provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session)); - Http01Challenge challenge = Challenge.bind(session, locationUri); + Http01Challenge challenge = Challenge.bind(session, locationUrl); assertThat(challenge.getType(), is(Http01Challenge.TYPE)); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); assertThat(challenge.getToken(), is("IlirfxKKXAsHtmzK29Pj8A")); provider.close(); @@ -111,7 +111,7 @@ public class ChallengeTest { // Test unmarshalled values assertThat(challenge.getType(), is("generic-01")); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(new URI("http://example.com/challenge/123"))); + assertThat(challenge.getLocation(), is(url("http://example.com/challenge/123"))); assertThat(challenge.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z"))); assertThat(challenge.getError(), is(notNullValue())); assertThat(challenge.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); @@ -154,8 +154,8 @@ public class ChallengeTest { public void testTrigger() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("triggerHttpChallengeRequest"))); assertThat(session, is(notNullValue())); } @@ -181,7 +181,7 @@ public class ChallengeTest { challenge.trigger(); assertThat(challenge.getStatus(), is(Status.PENDING)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -193,8 +193,8 @@ public class ChallengeTest { public void testUpdate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -223,7 +223,7 @@ public class ChallengeTest { challenge.update(); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -237,8 +237,8 @@ public class ChallengeTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -273,7 +273,7 @@ public class ChallengeTest { } assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -291,7 +291,7 @@ public class ChallengeTest { } try { - Challenge.bind(null, locationUri); + Challenge.bind(null, locationUrl); fail("session accepts null"); } catch (NullPointerException ex) { // expected @@ -305,8 +305,8 @@ public class ChallengeTest { public void testBadBind() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -323,7 +323,7 @@ public class ChallengeTest { }; Session session = provider.createSession(); - Challenge.bind(session, locationUri); + Challenge.bind(session, locationUrl); provider.close(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index 13ba9c3a..2f62140d 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -16,6 +16,7 @@ package org.shredzone.acme4j.connector; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.ByteArrayInputStream; @@ -29,7 +30,6 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -56,7 +56,7 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class DefaultConnectionTest { - private URI requestUri = URI.create("http://example.com/acme/");; + private URL requestUrl = url("http://example.com/acme/"); private HttpURLConnection mockUrlConnection; private HttpConnector mockHttpConnection; private Session session; @@ -66,7 +66,7 @@ public class DefaultConnectionTest { mockUrlConnection = mock(HttpURLConnection.class); mockHttpConnection = mock(HttpConnector.class); - when(mockHttpConnection.openConnection(requestUri)).thenReturn(mockUrlConnection); + when(mockHttpConnection.openConnection(requestUrl)).thenReturn(mockUrlConnection); session = TestUtils.session(); session.setLocale(Locale.JAPAN); @@ -144,8 +144,8 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); - assertThat(location, is(new URI("https://example.com/otherlocation"))); + URL location = conn.getLocation(); + assertThat(location, is(url("https://example.com/otherlocation"))); } verify(mockUrlConnection).getHeaderField("Location"); @@ -163,8 +163,8 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); - assertThat(location, is(new URI("https://example.org/otherlocation"))); + URL location = conn.getLocation(); + assertThat(location, is(url("https://example.org/otherlocation"))); } verify(mockUrlConnection).getHeaderField("Location"); @@ -191,48 +191,15 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - assertThat(conn.getLink("next"), is(new URI("https://example.com/acme/new-authz"))); - assertThat(conn.getLink("recover"), is(new URI("https://example.org/recover-reg"))); - assertThat(conn.getLink("terms-of-service"), is(new URI("https://example.com/acme/terms"))); + assertThat(conn.getLink("next"), is(url("https://example.com/acme/new-authz"))); + assertThat(conn.getLink("recover"), is(url("https://example.org/recover-reg"))); + assertThat(conn.getLink("terms-of-service"), is(url("https://example.com/acme/terms"))); assertThat(conn.getLink("secret-stuff"), is(nullValue())); - } - } - /** - * Test that multiple link headers are evaluated. - */ - @Test - public void testGetMultiLink() { - Map> headers = new HashMap<>(); - headers.put("Link", Arrays.asList( - "; rel=\"terms-of-service\"", - "; rel=\"terms-of-service\"", - "; rel=\"terms-of-service\"" - )); - - when(mockUrlConnection.getHeaderFields()).thenReturn(headers); - - try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.conn = mockUrlConnection; - assertThat(conn.getLinks("terms-of-service"), containsInAnyOrder( - URI.create("https://example.com/acme/terms1"), - URI.create("https://example.com/acme/terms2"), - URI.create("https://example.com/acme/terms3") - )); - } - } - - /** - * Test that no link headers are properly handled. - */ - @Test - public void testGetNoLink() { - Map> headers = Collections.emptyMap(); - when(mockUrlConnection.getHeaderFields()).thenReturn(headers); - - try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.conn = mockUrlConnection; - assertThat(conn.getLinks("something"), is(nullValue())); + assertThat(conn.getLinkAsURI("next"), is(new URI("https://example.com/acme/new-authz"))); + assertThat(conn.getLinkAsURI("recover"), is(new URI("https://example.org/recover-reg"))); + assertThat(conn.getLinkAsURI("terms-of-service"), is(new URI("https://example.com/acme/terms"))); + assertThat(conn.getLinkAsURI("secret-stuff"), is(nullValue())); } } @@ -243,7 +210,7 @@ public class DefaultConnectionTest { public void testNoLocation() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); + URL location = conn.getLocation(); assertThat(location, is(nullValue())); } @@ -488,7 +455,7 @@ public class DefaultConnectionTest { @Test public void testSendRequest() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.sendRequest(requestUri, session); + conn.sendRequest(requestUrl, session); } verify(mockUrlConnection).setRequestMethod("GET"); @@ -526,7 +493,7 @@ public class DefaultConnectionTest { }) { JSONBuilder cb = new JSONBuilder(); cb.put("foo", 123).put("bar", "a-string"); - conn.sendSignedRequest(requestUri, cb, DefaultConnectionTest.this.session); + conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session); } verify(mockUrlConnection).setRequestMethod("HEAD"); @@ -551,7 +518,7 @@ public class DefaultConnectionTest { StringBuilder expectedHeader = new StringBuilder(); expectedHeader.append('{'); expectedHeader.append("\"nonce\":\"").append(Base64Url.encode(nonce1)).append("\","); - expectedHeader.append("\"url\":\"").append(requestUri).append("\","); + expectedHeader.append("\"url\":\"").append(requestUrl).append("\","); expectedHeader.append("\"alg\":\"RS256\","); expectedHeader.append("\"jwk\":{"); expectedHeader.append("\"kty\":\"").append(TestUtils.KTY).append("\","); @@ -576,7 +543,7 @@ public class DefaultConnectionTest { public void testSendSignedRequestNoNonce() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { JSONBuilder cb = new JSONBuilder(); - conn.sendSignedRequest(requestUri, cb, DefaultConnectionTest.this.session); + conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java index e58574d7..23a811bf 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java @@ -14,8 +14,8 @@ package org.shredzone.acme4j.connector; import java.net.URI; +import java.net.URL; import java.security.cert.X509Certificate; -import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; @@ -29,12 +29,12 @@ import org.shredzone.acme4j.toolbox.JSONBuilder; public class DummyConnection implements Connection { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { throw new UnsupportedOperationException(); } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { throw new UnsupportedOperationException(); } @@ -64,17 +64,17 @@ public class DummyConnection implements Connection { } @Override - public URI getLocation() { + public URL getLocation() { throw new UnsupportedOperationException(); } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { throw new UnsupportedOperationException(); } @Override - public Collection getLinks(String relation) { + public URI getLinkAsURI(String relation) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java index 990a8fdc..1b372730 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java @@ -17,10 +17,10 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import org.junit.Test; @@ -59,7 +59,7 @@ public class HttpConnectorTest { @Category(HttpURLConnection.class) public void testOpenConnection() throws IOException, URISyntaxException { HttpConnector connector = new HttpConnector(); - HttpURLConnection conn = connector.openConnection(new URI("http://example.com")); + HttpURLConnection conn = connector.openConnection(url("http://example.com")); assertThat(conn, not(nullValue())); conn.connect(); assertThat(conn.getResponseCode(), is(HttpURLConnection.HTTP_OK)); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java index 1bb6059f..afb50fe6 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java @@ -15,11 +15,11 @@ package org.shredzone.acme4j.connector; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.toolbox.TestUtils.isIntArrayContainingInAnyOrder; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -43,19 +43,19 @@ public class ResourceIteratorTest { private final int RESOURCES_PER_PAGE = 5; private final String TYPE = "authorizations"; - private List resourceURIs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE); - private List pageURIs = new ArrayList<>(PAGES); + private List resourceURLs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE); + private List pageURLs = new ArrayList<>(PAGES); @Before public void setup() { - resourceURIs.clear(); + resourceURLs.clear(); for (int ix = 0; ix < RESOURCES_PER_PAGE * PAGES; ix++) { - resourceURIs.add(URI.create("https://example.com/acme/auth/" + ix)); + resourceURLs.add(url("https://example.com/acme/auth/" + ix)); } - pageURIs.clear(); + pageURLs.clear(); for (int ix = 0; ix < PAGES; ix++) { - pageURIs.add(URI.create("https://example.com/acme/batch/" + ix)); + pageURLs.add(url("https://example.com/acme/batch/" + ix)); } } @@ -76,14 +76,14 @@ public class ResourceIteratorTest { */ @Test public void iteratorTest() throws IOException { - List result = new ArrayList<>(); + List result = new ArrayList<>(); - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); while (it.hasNext()) { result.add(it.next().getLocation()); } - assertThat(result, is(equalTo(resourceURIs))); + assertThat(result, is(equalTo(resourceURLs))); } /** @@ -91,9 +91,9 @@ public class ResourceIteratorTest { */ @Test public void nextHasNextTest() throws IOException { - List result = new ArrayList<>(); + List result = new ArrayList<>(); - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); assertThat(it.hasNext(), is(true)); assertThat(it.hasNext(), is(true)); @@ -107,7 +107,7 @@ public class ResourceIteratorTest { assertThat(it.hasNext(), is(false)); } - assertThat(result, is(equalTo(resourceURIs))); + assertThat(result, is(equalTo(resourceURLs))); } /** @@ -115,7 +115,7 @@ public class ResourceIteratorTest { */ @Test(expected = UnsupportedOperationException.class) public void removeTest() throws IOException { - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); it.next(); it.remove(); // throws UnsupportedOperationException } @@ -127,13 +127,13 @@ public class ResourceIteratorTest { * URI of the first page * @return Created {@link Iterator} */ - private Iterator createIterator(URI first) throws IOException { + private Iterator createIterator(URL first) throws IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { private int ix; @Override - public void sendRequest(URI uri, Session session) { - ix = pageURIs.indexOf(uri); + public void sendRequest(URL url, Session session) { + ix = pageURLs.indexOf(url); assertThat(ix, is(greaterThanOrEqualTo(0))); } @@ -149,15 +149,15 @@ public class ResourceIteratorTest { int end = (ix + 1) * RESOURCES_PER_PAGE; JSONBuilder cb = new JSONBuilder(); - cb.array(TYPE, resourceURIs.subList(start, end).toArray()); + cb.array(TYPE, resourceURLs.subList(start, end).toArray()); return JSON.parse(cb.toString()); } @Override - public URI getLink(String relation) { - if ("next".equals(relation) && (ix + 1 < pageURIs.size())) { - return pageURIs.get(ix + 1); + public URL getLink(String relation) { + if ("next".equals(relation) && (ix + 1 < pageURLs.size())) { + return pageURLs.get(ix + 1); } return null; } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java index 694af22d..0c6e8004 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertThat; import java.io.IOException; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.util.ServiceLoader; @@ -92,7 +93,7 @@ public class SessionProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } @@ -120,7 +121,7 @@ public class SessionProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java index dd8ec815..4e1aef7b 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java @@ -15,8 +15,9 @@ package org.shredzone.acme4j.exception; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.shredzone.acme4j.toolbox.TestUtils.url; -import java.net.URI; +import java.net.URL; import org.junit.Test; @@ -28,13 +29,13 @@ public class AcmeConflictExceptionTest { @Test public void testAcmeConflictException() { String msg = "Account already exists"; - URI locationUri = URI.create("http://example.com/location/123"); + URL locationUrl = url("http://example.com/location/123"); AcmeConflictException ex - = new AcmeConflictException(msg, locationUri); + = new AcmeConflictException(msg, locationUrl); assertThat(ex.getMessage(), is(msg)); - assertThat(ex.getLocation(), is(locationUri)); + assertThat(ex.getLocation(), is(locationUrl)); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java index b233d4c1..444f8111 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java @@ -17,11 +17,12 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; @@ -56,7 +57,7 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } @@ -79,7 +80,7 @@ public class AbstractAcmeProviderTest { @Test public void testResources() throws Exception { final URI testServerUri = new URI("http://example.com/acme"); - final URI testResolvedUri = new URI("http://example.com/acme/directory"); + final URL testResolvedUrl = url("http://example.com/acme/directory"); final Connection connection = mock(Connection.class); final Session session = mock(Session.class); @@ -99,16 +100,16 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { assertThat(serverUri, is(testServerUri)); - return testResolvedUri; + return testResolvedUrl; } }; JSON map = provider.directory(session, testServerUri); assertThat(map.toString(), sameJSONAs(TestUtils.getJson("directory"))); - verify(connection).sendRequest(testResolvedUri, session); + verify(connection).sendRequest(testResolvedUrl, session); verify(connection).accept(any(Integer.class)); verify(connection).updateSession(any(Session.class)); verify(connection).readJsonResponse(); @@ -131,7 +132,7 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } }; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java index 257b1e58..b91f700d 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java @@ -16,8 +16,10 @@ package org.shredzone.acme4j.provider; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import org.junit.Test; @@ -42,13 +44,13 @@ public class GenericAcmeProviderTest { * Test if the provider resolves the URI correctly. */ @Test - public void testResolve() throws URISyntaxException { + public void testResolve() throws URISyntaxException, MalformedURLException { URI serverUri = new URI("http://example.com/acme"); GenericAcmeProvider provider = new GenericAcmeProvider(); - URI resolvedUri = provider.resolve(serverUri); - assertThat(resolvedUri, is(equalTo(serverUri))); + URL resolvedUrl = provider.resolve(serverUri); + assertThat(resolvedUrl, is(equalTo(serverUri.toURL()))); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java index f0d7566b..b80f2117 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider; import java.io.IOException; import java.net.URI; +import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -42,9 +43,9 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP * @param r * {@link Resource} to be mapped * @param u - * {@link URI} to be returned + * {@link URL} to be returned */ - public void putTestResource(Resource r, URI u) { + public void putTestResource(Resource r, URL u) { directory.put(r.path(), u); } @@ -75,7 +76,7 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java index 3454b5d2..2bff358c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.net.URI; import java.net.URISyntaxException; @@ -52,10 +53,10 @@ public class LetsEncryptAcmeProviderTest { public void testResolve() throws URISyntaxException { LetsEncryptAcmeProvider provider = new LetsEncryptAcmeProvider(); - assertThat(provider.resolve(new URI("acme://letsencrypt.org")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/v01")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/staging")), is(new URI(STAGING_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/v01")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/staging")), is(url(STAGING_DIRECTORY_URI))); try { provider.resolve(new URI("acme://letsencrypt.org/v99")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java index 09a17288..2adbebaf 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java @@ -15,10 +15,10 @@ package org.shredzone.acme4j.provider.letsencrypt; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import javax.net.ssl.HttpsURLConnection; @@ -46,7 +46,7 @@ public class LetsEncryptHttpConnectorTest { try { HttpURLConnection goodConn = connector.openConnection( - new URI("https://acme-staging.api.letsencrypt.org/directory")); + url("https://acme-staging.api.letsencrypt.org/directory")); assertThat(goodConn, is(instanceOf(HttpsURLConnection.class))); goodConn.connect(); } catch (SSLHandshakeException ex) { @@ -55,7 +55,7 @@ public class LetsEncryptHttpConnectorTest { try { HttpURLConnection badConn = connector.openConnection( - new URI("https://www.google.com")); + url("https://www.google.com")); assertThat(badConn, is(instanceOf(HttpsURLConnection.class))); badConn.connect(); fail("Connection accepts foreign certificate"); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java index 560a600c..e1e00ad3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java @@ -17,7 +17,9 @@ import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import java.security.InvalidAlgorithmParameterException; import java.security.KeyFactory; import java.security.KeyPair; @@ -112,6 +114,22 @@ public final class TestUtils { return JSON.parse(getJson(key)); } + /** + * Creates an {@link URL} from a String. Only throws a runtime exception if the URL is + * malformed. + * + * @param url + * URL to use + * @return {@link URL} object + */ + public static URL url(String url) { + try { + return new URL(url); + } catch (MalformedURLException ex) { + throw new IllegalArgumentException(url, ex); + } + } + /** * Creates a {@link Session} instance. It uses {@link #ACME_SERVER_URI} as server URI. */ diff --git a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java index a812b9b0..3aa56192 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -110,7 +110,7 @@ public class ClientTest { Certificate certificate = reg.requestCertificate(csrb.getEncoded()); LOG.info("Success! The certificate for domains " + domains + " has been generated!"); - LOG.info("Certificate URI: " + certificate.getLocation()); + LOG.info("Certificate URL: " + certificate.getLocation()); // Download the leaf certificate and certificate chain. X509Certificate cert = certificate.download(); @@ -151,9 +151,9 @@ public class ClientTest { * created. *

* This is a simple way of finding your {@link Registration}. A better way is to get - * the URI of your new registration with {@link Registration#getLocation()} and store + * the URL of your new registration with {@link Registration#getLocation()} and store * it somewhere. If you need to get access to your account later, reconnect to it via - * {@link Registration#bind(Session, URI)} by using the stored location. + * {@link Registration#bind(Session, URL)} by using the stored location. * * @param session * {@link Session} to bind with @@ -165,7 +165,7 @@ public class ClientTest { try { // Try to create a new Registration. reg = new RegistrationBuilder().create(session); - LOG.info("Registered a new user, URI: " + reg.getLocation()); + LOG.info("Registered a new user, URL: " + reg.getLocation()); // This is a new account. Let the user accept the Terms of Service. // We won't be able to authorize domains until the ToS is accepted. @@ -177,7 +177,7 @@ public class ClientTest { // The Key Pair is already registered. getLocation() contains the // URL of the existing registration's location. Bind it to the session. reg = Registration.bind(session, ex.getLocation()); - LOG.info("Account does already exist, URI: " + reg.getLocation(), ex); + LOG.info("Account does already exist, URL: " + reg.getLocation(), ex); } return reg; diff --git a/src/site/markdown/migration.md b/src/site/markdown/migration.md index 5e0ed548..5ee6ef8d 100644 --- a/src/site/markdown/migration.md +++ b/src/site/markdown/migration.md @@ -2,6 +2,10 @@ This document will help you migrate your code to the latest _acme4j_ version. +## Migration to Version 0.14 + +All resource locations are now `URL` objects (previously they were `URI`s). This should affect your source only in a minimum way, if any. + ## Migration to Version 0.13 In the `acme4j-client` module, the `org.shredzone.acme4j.util` package has been renamed in order to fix a split package in Java 9. This package is meant to be internal, so this change should not break your code. The same named public package in the `acme4j-utils` module is unchanged. diff --git a/src/site/markdown/provider.md b/src/site/markdown/provider.md index b813d21d..b3a2c673 100644 --- a/src/site/markdown/provider.md +++ b/src/site/markdown/provider.md @@ -21,11 +21,11 @@ However, it is also possible to adapt the behavior of wide parts of _acme4j_ to A client provider implements the [`AcmeProvider`](./apidocs/org/shredzone/acme4j/provider/AcmeProvider.html) interface, but usually it is easier to extend [`AbstractAcmeProvider`](./apidocs/org/shredzone/acme4j/provider/AbstractAcmeProvider.html) and implement only these two methods: * `accepts(URI)` checks if the client provider is accepting the provided URI. Usually it would be an URI like `acme://example.com`. Note that the `http` and `https` schemes are reserved for the generic provider and cannot be used by other providers. -* `resolve(URI)` parses that URI and returns the corresponding URI of the directory service. +* `resolve(URI)` parses that URI and returns the corresponding URL of the directory service. The `AcmeProvider` implementation needs to be registered with Java's `ServiceLoader`. In the `META-INF/services` path of your project, create a file `org.shredzone.acme4j.provider.AcmeProvider` and write the fully qualified class name of your implementation into that file. -When _acme4j_ tries to connect to an acme URI, it first invokes the `accepts(URI)` method of all registered `AcmeProvider`s. Only one of the providers must return `true` for a successful connection. _acme4j_ then invokes the `resolve(URI)` method of that provider, and connects to the directory URI that is returned. +When _acme4j_ tries to connect to an acme URI, it first invokes the `accepts(URI)` method of all registered `AcmeProvider`s. Only one of the providers must return `true` for a successful connection. _acme4j_ then invokes the `resolve(URI)` method of that provider, and connects to the directory URL that is returned. The connection fails if none or more than one `AcmeProvider` implementations `accept` the acme URI. @@ -43,6 +43,6 @@ In your `AcmeProvider` implementation, override the `createChallenge(Session, St ## No directory service -An ACME server may not provide a directory service, for example when fixed URIs are to be used. +An ACME server may not provide a directory service, for example when fixed URLs are to be used. -In this case, override the `resources(Session, URI)` method, and return a `Map` of all available resources and their respective URI. +In this case, override the `resources(Session, URI)` method, and return a `Map` of all available resources and their respective URL. diff --git a/src/site/markdown/usage/authorization.md b/src/site/markdown/usage/authorization.md index da5f9c3a..ca0995e8 100644 --- a/src/site/markdown/usage/authorization.md +++ b/src/site/markdown/usage/authorization.md @@ -56,12 +56,12 @@ If your final certificate will contain further domains or subdomains, repeat the ## Update an Authorization -The server also provides an authorization URI. It can be retrieved from `Authorization.getLocation()`. You can recreate the `Authorization` object at a later time just by binding it to your `Session`: +The server also provides an authorization URL. It can be retrieved from `Authorization.getLocation()`. You can recreate the `Authorization` object at a later time just by binding it to your `Session`: ```java -URI authUri = ... // Authorization URI +URL authUrl = ... // Authorization URL -Authorization auth = Authorization.bind(session, authUri); +Authorization auth = Authorization.bind(session, authUrl); ``` As soon as you invoke a getter, the `Authorization` object lazily loads the current server state of your authorization, including the domain name, the overall status, and an expiry date. @@ -84,14 +84,14 @@ To recreate a `Challenge` object at a later time, all you need is to store the o ```java Challenge originalChallenge = ... // some Challenge instance -URI challengeUri = originalChallenge.getLocation(); +URL challengeUrl = originalChallenge.getLocation(); ``` Later, you restore the `Challenge` object by invoking `Challenge.bind()`. ```java -URI challengeUri = ... // challenge URI -Challenge restoredChallenge = Challenge.bind(session, challengeUri); +URL challengeUrl = ... // challenge URL +Challenge restoredChallenge = Challenge.bind(session, challengeUrl); ``` The `restoredChallenge` already reflects the current state of the challenge. diff --git a/src/site/markdown/usage/certificate.md b/src/site/markdown/usage/certificate.md index 7fff510a..c1c87183 100644 --- a/src/site/markdown/usage/certificate.md +++ b/src/site/markdown/usage/certificate.md @@ -34,7 +34,7 @@ Now all you need to do is to pass in a binary representation of the CSR and requ Certificate cert = registration.requestCertificate(csr); ``` -`cert.getLocation()` returns an URI where the signed certificate can be downloaded from. Optionally (if delivered by the ACME server) `cert.getChainLocation()` returns the URI of the first part of the CA chain. +`cert.getLocation()` returns an URL where the signed certificate can be downloaded from. Optionally (if delivered by the ACME server) `cert.getChainLocation()` returns the URL of the first part of the CA chain. The `Certificate` object offers methods to download the certificate and the certificate chain. @@ -50,8 +50,8 @@ Congratulations! You have just created your first certificate via _acme4j_. To recreate a `Certificate` object from the location, just bind it: ```java -URI locationUri = ... // location URI from cert.getLocation() -Certificate cert = Certificate.bind(session, locationUri); +URL locationUrl = ... // location URL from cert.getLocation() +Certificate cert = Certificate.bind(session, locationUrl); ``` ### Saving Certificates diff --git a/src/site/markdown/usage/register.md b/src/site/markdown/usage/register.md index 3f3ba1dd..5ea3ca54 100644 --- a/src/site/markdown/usage/register.md +++ b/src/site/markdown/usage/register.md @@ -2,7 +2,7 @@ If it is the first time you connect to the ACME server, you need to register your account key. -To do so, create a `RegistrationBuilder`, optionally add some contact information, then invoke `create()`. If the account was successfully created, you will get a `Registration` object in return. Invoking its `getLocation()` method will return the location URI of your account. You should store it somewhere, because you will need it later. Unlike your key pair, the location is a public information that does not need security precautions. +To do so, create a `RegistrationBuilder`, optionally add some contact information, then invoke `create()`. If the account was successfully created, you will get a `Registration` object in return. Invoking its `getLocation()` method will return the location URL of your account. You should store it somewhere, because you will need it later. Unlike your key pair, the location is a public information that does not need security precautions. ```java RegistrationBuilder builder = new RegistrationBuilder(); @@ -10,10 +10,10 @@ builder.addContact("mailto:acme@example.com"); Registration registration = builder.create(session); -URI accountLocationUri = registration.getLocation(); +URL accountLocationUrl = registration.getLocation(); ``` -`create()` will fail and throw an `AcmeConflictException` if your key was already registered with the CA. The `AcmeConflictException` contains the location of the registration. This may be helpful if you forgot your account URI and need to recover it. +`create()` will fail and throw an `AcmeConflictException` if your key was already registered with the CA. The `AcmeConflictException` contains the location of the registration. This may be helpful if you forgot your account URL and need to recover it. The following example will create a new `Registration` and restore an existing `Registration`. diff --git a/src/site/markdown/usage/session.md b/src/site/markdown/usage/session.md index db415b35..39cce1b7 100644 --- a/src/site/markdown/usage/session.md +++ b/src/site/markdown/usage/session.md @@ -31,12 +31,12 @@ Instead of a generic provider, this call uses a special _Let's Encrypt_ provider Now that you have a `Session` object, you can use it to bind ACME resource objects. For example, this is the way to get a `Registration` object to an existing registration: ```java -URI accountLocationUri = ... // your account's URI, as returned by Registration.getLocation() +URL accountLocationUrl = ... // your account's URL, as returned by Registration.getLocation() -Registration registration = Registration.bind(session, accountLocationUri); +Registration registration = Registration.bind(session, accountLocationUrl); ``` -You can create any of the resource objects `Registration`, `Authorization`, `Challenge` and `Certificate` like that, as long as you know the corresponding resource URI. To get the resource URI, use the `getLocation()` method. +You can create any of the resource objects `Registration`, `Authorization`, `Challenge` and `Certificate` like that, as long as you know the corresponding resource URL. To get the resource URL, use the `getLocation()` method. ## Serialization From e4d7666d5f15c8f6ee91193097c1b7f4406c03e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 4 Nov 2017 13:04:49 +0100 Subject: [PATCH 27/38] Exclude example from Sonar tests --- acme4j-example/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 8d684907..128a74c9 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -28,6 +28,11 @@ acme4j Example Example for using acme4j + + + true + + From 488c54fff0a012d819ab9eca09429e1fd02fe1eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 4 Nov 2017 13:05:20 +0100 Subject: [PATCH 28/38] Sonar fixes --- acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java | 4 +++- .../src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java index b9bbe8b9..fcef79d2 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Problem.java @@ -53,7 +53,9 @@ public class Problem implements Serializable { } /** - * Returns the problem as {@link JSON} object, to access other fields. + * Returns the problem object, to access custom fields. + * + * @return Problem, as {@link JSON} object */ public JSON asJSON() { return problemJson; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java index ee8af251..22368921 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/toolbox/AcmeUtils.java @@ -51,7 +51,7 @@ public final class AcmeUtils { private static final Pattern TZ_PATTERN = Pattern.compile( "([+-])(\\d{2}):?(\\d{2})$"); - private final static Pattern CONTENT_TYPE_PATTERN = Pattern.compile( + private static final Pattern CONTENT_TYPE_PATTERN = Pattern.compile( "([^;]+)(?:;.*?charset=(\"?)([a-z0-9_-]+)(\\2))?.*", Pattern.CASE_INSENSITIVE); private AcmeUtils() { From ae96894c0964204fb119d193ef13cec273d0c8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 4 Nov 2017 13:05:27 +0100 Subject: [PATCH 29/38] Don't fail if version.properties is unreadable Fixes issue #42 --- .../main/java/org/shredzone/acme4j/connector/HttpConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java index ecfe6fea..79b60f3d 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java @@ -40,7 +40,7 @@ public class HttpConnector { Properties prop = new Properties(); prop.load(in); agent.append('/').append(prop.getProperty("version")); - } catch (IOException ex) { + } catch (Exception ex) { // Ignore, just don't use a version LoggerFactory.getLogger(HttpConnector.class).warn("Could not read library version", ex); } From 544be26376895ab04ba327684288797915d14812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sat, 4 Nov 2017 13:50:33 +0100 Subject: [PATCH 30/38] Un-deprecate tls-sni-01 It is likely to stay in ACMEv1. --- .../org/shredzone/acme4j/challenge/TlsSni01Challenge.java | 5 ----- .../org/shredzone/acme4j/provider/AbstractAcmeProvider.java | 4 ++-- .../shredzone/acme4j/challenge/TlsSni01ChallengeTest.java | 1 - .../shredzone/acme4j/provider/AbstractAcmeProviderTest.java | 1 - .../src/main/java/org/shredzone/acme4j/ClientTest.java | 1 - .../java/org/shredzone/acme4j/util/CertificateUtils.java | 3 --- src/site/markdown/challenge/tls-sni-01.md | 2 +- src/site/markdown/challenge/tls-sni-02.md | 2 +- 8 files changed, 4 insertions(+), 15 deletions(-) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java index cbb88814..c4397c09 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/TlsSni01Challenge.java @@ -19,12 +19,7 @@ import org.shredzone.acme4j.Session; /** * Implements the {@value TYPE} challenge. - * - * @deprecated Use {@link TlsSni02Challenge} if supported by the CA. This challenge will - * be removed when Let's Encrypt removes support for - * {@link TlsSni01Challenge}. */ -@Deprecated public class TlsSni01Challenge extends TokenChallenge { private static final long serialVersionUID = 7370329525205430573L; diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java index cbc9e7a3..d649cd28 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeProvider.java @@ -26,6 +26,7 @@ import org.shredzone.acme4j.challenge.Challenge; import org.shredzone.acme4j.challenge.Dns01Challenge; import org.shredzone.acme4j.challenge.Http01Challenge; import org.shredzone.acme4j.challenge.OutOfBand01Challenge; +import org.shredzone.acme4j.challenge.TlsSni01Challenge; import org.shredzone.acme4j.challenge.TlsSni02Challenge; import org.shredzone.acme4j.connector.Connection; import org.shredzone.acme4j.connector.DefaultConnection; @@ -62,12 +63,11 @@ public abstract class AbstractAcmeProvider implements AcmeProvider { } } - @SuppressWarnings("deprecation") // must still provide deprecated challenges private static Map> challengeMap() { Map> map = new HashMap<>(); map.put(Dns01Challenge.TYPE, Dns01Challenge::new); - map.put(org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE, org.shredzone.acme4j.challenge.TlsSni01Challenge::new); + map.put(TlsSni01Challenge.TYPE, TlsSni01Challenge::new); map.put(TlsSni02Challenge.TYPE, TlsSni02Challenge::new); map.put(Http01Challenge.TYPE, Http01Challenge::new); map.put(OutOfBand01Challenge.TYPE, OutOfBand01Challenge::new); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java index f317bb84..dbc40a83 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/TlsSni01ChallengeTest.java @@ -30,7 +30,6 @@ import org.shredzone.acme4j.toolbox.TestUtils; /** * Unit tests for {@link TlsSni01Challenge}. */ -@SuppressWarnings("deprecation") // must test a deprecated challenge public class TlsSni01ChallengeTest { private static final String KEY_AUTHORIZATION = "VNLBdSiZ3LppU2CRG8bilqlwq4DuApJMg3ZJowU6JhQ.HnWjTDnyqlCrm6tZ-6wX-TrEXgRdeNu9G71gqxSO6o0"; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java index 444f8111..0a22ab45 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java @@ -121,7 +121,6 @@ public class AbstractAcmeProviderTest { * Test that challenges are generated properly. */ @Test - @SuppressWarnings("deprecation") // must test deprecated challenges public void testCreateChallenge() { Session session = mock(Session.class); diff --git a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java index 3aa56192..6bd896f0 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -344,7 +344,6 @@ public class ClientTest { * Domain name to be authorized * @return {@link Challenge} to verify */ - @SuppressWarnings("deprecation") // until tls-sni-02 is supported public Challenge tlsSniChallenge(Authorization auth, String domain) throws AcmeException { // Find a single tls-sni-01 challenge org.shredzone.acme4j.challenge.TlsSni01Challenge challenge = auth.findChallenge(org.shredzone.acme4j.challenge.TlsSni01Challenge.TYPE); diff --git a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java index c0169a3a..276e5e25 100644 --- a/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java +++ b/acme4j-utils/src/main/java/org/shredzone/acme4j/util/CertificateUtils.java @@ -177,10 +177,7 @@ public final class CertificateUtils { * @param subject * Subject to create a certificate for * @return Created certificate - * @deprecated Will be removed when - * {@link org.shredzone.acme4j.challenge.TlsSni01Challenge} is removed */ - @Deprecated public static X509Certificate createTlsSniCertificate(KeyPair keypair, String subject) throws IOException { return createCertificate(keypair, subject); } diff --git a/src/site/markdown/challenge/tls-sni-01.md b/src/site/markdown/challenge/tls-sni-01.md index 0b8b2c33..510666cb 100644 --- a/src/site/markdown/challenge/tls-sni-01.md +++ b/src/site/markdown/challenge/tls-sni-01.md @@ -1,6 +1,6 @@ # tls-sni-01 Challenge -> **DEPRECATED:** According to the ACME specifications, this challenge will be replaced by [tls-sni-02](./tls-sni-02.html). However, _Let's Encrypt_ does not currently support `tls-sni-02`. For the time being, _acme4j_ supports both challenges. To be on the safe side, request both challenges and process the one that is returned. +> **NOTE:** In ACMEv2, this challenge is going to be replaced by [tls-sni-02](./tls-sni-02.html). However, the _Let's Encrypt_ ACMEv1 server is still offering this challenge as the only TLS-SNI based challenge. To be on the safe side, request both challenges and process the one that is returned. With the `tls-sni-01` challenge, you prove to the CA that you are able to control the web server of the domain to be authorized, by letting it respond to a SNI request with a specific self-signed cert. diff --git a/src/site/markdown/challenge/tls-sni-02.md b/src/site/markdown/challenge/tls-sni-02.md index f7979954..457537b5 100644 --- a/src/site/markdown/challenge/tls-sni-02.md +++ b/src/site/markdown/challenge/tls-sni-02.md @@ -1,6 +1,6 @@ # tls-sni-02 Challenge -> **NOTE:** According to the ACME specifications, this challenge will replace [tls-sni-01](./tls-sni-01.html). However, _Let's Encrypt_ does not currently support `tls-sni-02`. For the time being, _acme4j_ supports both challenges. To be on the safe side, request both challenges and process the one that is returned. +> **NOTE:** According to the ACME specifications, this challenge will replace [tls-sni-01](./tls-sni-01.html). However, _Let's Encrypt_ does not currently support `tls-sni-02`. To be on the safe side, request both challenges and process the one that is returned. With the `tls-sni-02` challenge, you prove to the CA that you are able to control the web server of the domain to be authorized, by letting it respond to a SNI request with a specific self-signed cert. From 89155da93f372f276511fa3875aef520b25e835d Mon Sep 17 00:00:00 2001 From: Ivan Wallis Date: Wed, 15 Nov 2017 20:53:48 -0800 Subject: [PATCH 31/38] looping through chain will hit recursion limit since updated link is not being sent as a request. --- .../src/main/java/org/shredzone/acme4j/Certificate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e6314fae..aafc9204 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -126,7 +126,7 @@ public class Certificate extends AcmeResource { URL link = chainCertUrl; while (link != null && certChain.size() < MAX_CHAIN_LENGTH) { try (Connection conn = getSession().provider().connect()) { - conn.sendRequest(chainCertUrl, getSession()); + conn.sendRequest(link, getSession()); conn.accept(HttpURLConnection.HTTP_OK); certChain.add(conn.readCertificate()); From 02460388f0e1fd31fdf3e5709413bd2b4d13f18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Thu, 16 Nov 2017 22:23:00 +0100 Subject: [PATCH 32/38] Update dependencies --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1871a88d..f253d9eb 100644 --- a/pom.xml +++ b/pom.xml @@ -52,7 +52,7 @@ 1.58 - 0.6.1 + 0.6.2 1.7.25 utf-8 From dae535b1ba56dab4945091230b5ef6be8aec1f27 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 4 Dec 2017 14:41:28 -0500 Subject: [PATCH 33/38] Fix minor provider.md typo The "Writing your own Provider" section of `src/site/markdown/provider.md` had a small typo "Writing an own Provider". This commit fixes the typo. --- src/site/markdown/provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/markdown/provider.md b/src/site/markdown/provider.md index b3a2c673..14ffd521 100644 --- a/src/site/markdown/provider.md +++ b/src/site/markdown/provider.md @@ -12,7 +12,7 @@ ACME providers are "plug-ins" to _acme4j_ that are specialized on a single CA. F Session session = new Session("acme://letsencrypt.org", accountKeyPair); ``` -## Writing an own Provider +## Writing your own Provider Every CA that provides an ACME server should also have an own `AcmeProvider`, and if it is just for the sake of a pretty `acme:` URI. From bb892dca1dc296193add09d45089cbf751840f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Mon, 4 Dec 2017 22:32:34 +0100 Subject: [PATCH 34/38] Recommend update of older acme4j versions --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 291e3e2b..decee7f8 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ It is an independent open source implementation that is not affiliated with or e * Supports the "ACME v1" protocol that is used by _Let's Encrypt_ * Easy to use Java API -* Requires JRE 8 or higher +* Requires JRE 8u101 or higher * Built with maven, packages available at [Maven Central](http://search.maven.org/#search|ga|1|g%3A%22org.shredzone.acme4j%22) * Small: `acme4j-client` only requires [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home) and [slf4j](http://www.slf4j.org/) as dependencies * Only the optional `acme4j-utils` module requires [Bouncy Castle](http://www.bouncycastle.org) @@ -23,6 +23,12 @@ It is an independent open source implementation that is not affiliated with or e * See the [online documentation](https://shredzone.org/maven/acme4j/) about how to use _acme4j_. * For a quick start, have a look at [the source code of an example](https://github.com/shred/acme4j/blob/master/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java). +## Important Note + +In order to connect to the _Let's Encrypt_ servers, _acme4j_ has used a local truststore containing their SSL certificate. Native support of IdenTrust certificates was added to Java 8u101 in July 2016. Since then, the local truststore was not necessary any more. It has been disabled in _acme4j_ v0.12. + +If you are still using _acme4j_ < v0.12, you should update to the latest version soon. The certificate in the local truststore expires on June 2018 (or maybe earlier, at the discretion of _Let's Encrypt_). + ## Future Compatibility This version of _acme4j_ is tailor-made for _Let's Encrypt_ and other CAs that use the [Boulder](https://github.com/letsencrypt/boulder) server and the "ACME v1" protocol. From 3542dd67fa3401a66659c83c6c6116cfa211bcf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Tue, 5 Dec 2017 00:02:46 +0100 Subject: [PATCH 35/38] Add github ribbon --- src/site/site.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/site/site.xml b/src/site/site.xml index 0c98a1f7..0183f16a 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -57,4 +57,13 @@ maven-fluido-skin 1.6 + + + + + shred/acme4j + right + + + From cd2009b95b0e0109954d2353ce2a1486c8f36876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 6 Dec 2017 00:16:23 +0100 Subject: [PATCH 36/38] [maven-release-plugin] prepare release v0.14 --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index 8fc05377..59df8508 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14-SNAPSHOT + 0.14 acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index 128a74c9..d300486b 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14-SNAPSHOT + 0.14 acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index 86b069d4..a3cfff86 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14-SNAPSHOT + 0.14 acme4j-utils diff --git a/pom.xml b/pom.xml index f253d9eb..bee09fea 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.14-SNAPSHOT + 0.14 pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - HEAD + v0.14 GitHub From 85d97a5b97014ff106b4867d8acf298c9b94e25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 6 Dec 2017 00:16:23 +0100 Subject: [PATCH 37/38] [maven-release-plugin] prepare for next development iteration --- acme4j-client/pom.xml | 2 +- acme4j-example/pom.xml | 2 +- acme4j-utils/pom.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme4j-client/pom.xml b/acme4j-client/pom.xml index 59df8508..06265510 100644 --- a/acme4j-client/pom.xml +++ b/acme4j-client/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14 + 0.15-SNAPSHOT acme4j-client diff --git a/acme4j-example/pom.xml b/acme4j-example/pom.xml index d300486b..83470169 100644 --- a/acme4j-example/pom.xml +++ b/acme4j-example/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14 + 0.15-SNAPSHOT acme4j-example diff --git a/acme4j-utils/pom.xml b/acme4j-utils/pom.xml index a3cfff86..926205f9 100644 --- a/acme4j-utils/pom.xml +++ b/acme4j-utils/pom.xml @@ -20,7 +20,7 @@ org.shredzone.acme4j acme4j - 0.14 + 0.15-SNAPSHOT acme4j-utils diff --git a/pom.xml b/pom.xml index bee09fea..18ad8250 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.shredzone.acme4j acme4j - 0.14 + 0.15-SNAPSHOT pom acme4j @@ -37,7 +37,7 @@ https://github.com/shred/acme4j/ scm:git:git@github.com:shred/acme4j.git scm:git:git@github.com:shred/acme4j.git - v0.14 + HEAD GitHub From 13d49b275be674689df2cdbd83100e760b50d7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Sun, 7 Jan 2018 14:21:46 +0100 Subject: [PATCH 38/38] Add issue template and contribution rules --- .github/ISSUE_TEMPLATE.md | 7 ++++++ CONTRIBUTING.md | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..99716abb --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,7 @@ + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3223c01b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +# Contributing to _acme4j_ + +Thank you for taking your time to contribute! + +## Acceptance Criteria + +These criteria must be met for a successful pull request: + +* Follow the [Style Guide](#Style-Guide). +* If you add code, remember to add [unit tests](#Unit-Tests) that test your code. +* All unit tests must run successfully. +* Integration tests should run successfully, unless there is a good reason (e.g. waiting for a pending change in Pebble). +* Your commits follow the [git commit](#git-Commits) guide. +* You accept that your code is distributed under the terms of [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0). + +## Style Guide + +Our style guide bases on [Oracle's Code conventions for the Java Programming Language](http://www.oracle.com/technetwork/java/codeconventions-150003.pdf). These additional rules apply: + +* Indentation is 4 spaces. Do not use tabs! +* Remove trailing spaces. +* Line length is 90 characters. You may exceed this length by a few characters if it is easier to read than a wrapped line. +* `if`, `for` and `while` statements always use blocks, even for a single statement. +* All types and methods must have a descriptive JavaDoc, except of `@Override` annotated methods. For plain getter and setter methods, `@param` and '@return' can be omitted. + +## Unit Tests + +More than 80% of the code is covered by unit tests, and we would like to keep it that way. + +* Main functionalities must be covered by unit tests. +* Corner cases should be covered by unit tests. +* Common exception handling does not need to be tested. +* No tests are required for code that is not expected to be executed (e.g. `UnsupportedEncodingException` when handling utf-8, or the empty private default constructor of a utility class). +* Unit tests should not depend on external resources, as they might be temporarily unavailable at runtime. + +There are no unit tests required for the `acme4j-example` and `acme4j-it` modules. + +## git Commits + +Good programming does not end with a clean source code, but should have pretty commits as well. + +* Always put separate concerns into separate commits. +* If you have interim commits in your history, squash them with an interactive rebase before sending the pull request. +* Use present tense and imperative mood in commit messages ("fix bug #1234", not "fixed bug #1234"). +* Always give meaningful commit messages (not just "bugfix"). +* The commit message must be concise and should not exceed 50 characters. Further explanations may follow in subsequent lines, with an empty line as separator. +* Commits must compile and must not break unit tests.