Move jwkThumbprint() to SignatureUtils, add unit test

pull/18/head
Richard Körber 2016-06-22 00:45:57 +02:00
parent ddac0c45d1
commit c48febda62
5 changed files with 54 additions and 36 deletions

View File

@ -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 <a href="https://tools.ietf.org/html/rfc7638">RFC 7638</a>
*/
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.
*/

View File

@ -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()));
}
}

View File

@ -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 <a href="https://tools.ietf.org/html/rfc7638">RFC 7638</a>
*/
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);
}
}
}

View File

@ -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)));
}

View File

@ -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));
}
}