diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericChallenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericChallenge.java
index 70872ac0..ecc261b8 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericChallenge.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericChallenge.java
@@ -16,19 +16,13 @@ package org.shredzone.acme4j.challenge;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.jose4j.json.JsonUtil;
-import org.jose4j.jwk.JsonWebKey;
-import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeProtocolException;
@@ -132,34 +126,6 @@ public class GenericChallenge implements Challenge {
return (T) data.get(key);
}
- /**
- * Computes a JWK Thumbprint. It is frequently used in responses.
- *
- * @param key
- * {@link PublicKey} to create a thumbprint of
- * @return Thumbprint, SHA-256 hashed
- * @see RFC 7638
- */
- public static byte[] jwkThumbprint(PublicKey key) {
- if (key == null) {
- throw new NullPointerException("key must not be null");
- }
-
- try {
- final JsonWebKey jwk = JsonWebKey.Factory.newJwk(key);
-
- // We need to use ClaimBuilder to bring the keys in lexicographical order.
- ClaimBuilder cb = new ClaimBuilder();
- cb.putAll(jwk.toParams(OutputControlLevel.PUBLIC_ONLY));
-
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(cb.toString().getBytes("UTF-8"));
- return md.digest();
- } catch (JoseException | NoSuchAlgorithmException | UnsupportedEncodingException ex) {
- throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
- }
- }
-
/**
* Serialize the data map in JSON.
*/
diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericTokenChallenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericTokenChallenge.java
index a8da438f..d485e2f2 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericTokenChallenge.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/GenericTokenChallenge.java
@@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import org.jose4j.base64url.Base64Url;
import org.shredzone.acme4j.Registration;
import org.shredzone.acme4j.util.ClaimBuilder;
+import org.shredzone.acme4j.util.SignatureUtils;
/**
* An extension of {@link GenericChallenge} that handles challenges with a {@code token}
@@ -94,7 +95,7 @@ public class GenericTokenChallenge extends GenericChallenge {
protected String computeAuthorization(Registration registration) {
return getToken()
+ '.'
- + Base64Url.encode(jwkThumbprint(registration.getKeyPair().getPublic()));
+ + Base64Url.encode(SignatureUtils.jwkThumbprint(registration.getKeyPair().getPublic()));
}
}
diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/util/SignatureUtils.java b/acme4j-client/src/main/java/org/shredzone/acme4j/util/SignatureUtils.java
index 3d8630d9..5929d748 100644
--- a/acme4j-client/src/main/java/org/shredzone/acme4j/util/SignatureUtils.java
+++ b/acme4j-client/src/main/java/org/shredzone/acme4j/util/SignatureUtils.java
@@ -13,11 +13,19 @@
*/
package org.shredzone.acme4j.util;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
import org.jose4j.jwk.EllipticCurveJsonWebKey;
import org.jose4j.jwk.JsonWebKey;
+import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.lang.JoseException;
+import org.shredzone.acme4j.exception.AcmeProtocolException;
/**
* Utility class for signatures.
@@ -67,4 +75,32 @@ public final class SignatureUtils {
}
}
+ /**
+ * Computes a JWK Thumbprint. It is frequently used in responses.
+ *
+ * @param key
+ * {@link PublicKey} to create a thumbprint of
+ * @return Thumbprint, SHA-256 hashed
+ * @see RFC 7638
+ */
+ public static byte[] jwkThumbprint(PublicKey key) {
+ if (key == null) {
+ throw new NullPointerException("key must not be null");
+ }
+
+ try {
+ final JsonWebKey jwk = JsonWebKey.Factory.newJwk(key);
+
+ // We need to use ClaimBuilder to bring the keys in lexicographical order.
+ ClaimBuilder cb = new ClaimBuilder();
+ cb.putAll(jwk.toParams(OutputControlLevel.PUBLIC_ONLY));
+
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(cb.toString().getBytes("UTF-8"));
+ return md.digest();
+ } catch (JoseException | NoSuchAlgorithmException | UnsupportedEncodingException ex) {
+ throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
+ }
+ }
+
}
diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java
index 1c4dcc87..2e2b3485 100644
--- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java
+++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/GenericChallengeTest.java
@@ -35,6 +35,7 @@ import org.junit.Test;
import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.util.ClaimBuilder;
+import org.shredzone.acme4j.util.SignatureUtils;
import org.shredzone.acme4j.util.TestUtils;
import org.shredzone.acme4j.util.TimestampParser;
@@ -115,7 +116,7 @@ public class GenericChallengeTest {
assertThat(cb.toString(), is(json.toString()));
// Make sure the returned thumbprint is correct
- byte[] thumbprint = GenericChallenge.jwkThumbprint(keypair.getPublic());
+ byte[] thumbprint = SignatureUtils.jwkThumbprint(keypair.getPublic());
assertThat(thumbprint, is(Base64Url.decode(TestUtils.THUMBPRINT)));
}
diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/util/SignatureUtilsTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/util/SignatureUtilsTest.java
index 885d730b..fbc1afb3 100644
--- a/acme4j-client/src/test/java/org/shredzone/acme4j/util/SignatureUtilsTest.java
+++ b/acme4j-client/src/test/java/org/shredzone/acme4j/util/SignatureUtilsTest.java
@@ -20,6 +20,7 @@ import java.security.KeyPair;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.jose4j.base64url.Base64Url;
import org.jose4j.jwk.PublicJsonWebKey;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -88,4 +89,17 @@ public class SignatureUtilsTest {
assertThat(type, is("ES512"));
}
+ /**
+ * Test if {@link SignatureUtils#jwkThumbprint(java.security.PublicKey)} returns the
+ * correct thumb print.
+ */
+ @Test
+ public void testJwkThumbprint() throws Exception {
+ KeyPair keyPair = TestUtils.createKeyPair();
+
+ byte[] thumbprint = SignatureUtils.jwkThumbprint(keyPair.getPublic());
+
+ assertThat(Base64Url.encode(thumbprint), is(TestUtils.THUMBPRINT));
+ }
+
}