mirror of https://github.com/shred/acme4j
Move jwkThumbprint() to SignatureUtils, add unit test
parent
ddac0c45d1
commit
c48febda62
|
@ -16,19 +16,13 @@ package org.shredzone.acme4j.challenge;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jose4j.json.JsonUtil;
|
import org.jose4j.json.JsonUtil;
|
||||||
import org.jose4j.jwk.JsonWebKey;
|
|
||||||
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
|
|
||||||
import org.jose4j.lang.JoseException;
|
import org.jose4j.lang.JoseException;
|
||||||
import org.shredzone.acme4j.Status;
|
import org.shredzone.acme4j.Status;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
@ -132,34 +126,6 @@ public class GenericChallenge implements Challenge {
|
||||||
return (T) data.get(key);
|
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.
|
* Serialize the data map in JSON.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
|
||||||
import org.jose4j.base64url.Base64Url;
|
import org.jose4j.base64url.Base64Url;
|
||||||
import org.shredzone.acme4j.Registration;
|
import org.shredzone.acme4j.Registration;
|
||||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||||
|
import org.shredzone.acme4j.util.SignatureUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extension of {@link GenericChallenge} that handles challenges with a {@code token}
|
* 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) {
|
protected String computeAuthorization(Registration registration) {
|
||||||
return getToken()
|
return getToken()
|
||||||
+ '.'
|
+ '.'
|
||||||
+ Base64Url.encode(jwkThumbprint(registration.getKeyPair().getPublic()));
|
+ Base64Url.encode(SignatureUtils.jwkThumbprint(registration.getKeyPair().getPublic()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,19 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.util;
|
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.EllipticCurveJsonWebKey;
|
||||||
import org.jose4j.jwk.JsonWebKey;
|
import org.jose4j.jwk.JsonWebKey;
|
||||||
|
import org.jose4j.jwk.JsonWebKey.OutputControlLevel;
|
||||||
import org.jose4j.jwk.RsaJsonWebKey;
|
import org.jose4j.jwk.RsaJsonWebKey;
|
||||||
import org.jose4j.jws.AlgorithmIdentifiers;
|
import org.jose4j.jws.AlgorithmIdentifiers;
|
||||||
import org.jose4j.jws.JsonWebSignature;
|
import org.jose4j.jws.JsonWebSignature;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for signatures.
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.junit.Test;
|
||||||
import org.shredzone.acme4j.Status;
|
import org.shredzone.acme4j.Status;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
import org.shredzone.acme4j.util.ClaimBuilder;
|
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||||
|
import org.shredzone.acme4j.util.SignatureUtils;
|
||||||
import org.shredzone.acme4j.util.TestUtils;
|
import org.shredzone.acme4j.util.TestUtils;
|
||||||
import org.shredzone.acme4j.util.TimestampParser;
|
import org.shredzone.acme4j.util.TimestampParser;
|
||||||
|
|
||||||
|
@ -115,7 +116,7 @@ public class GenericChallengeTest {
|
||||||
assertThat(cb.toString(), is(json.toString()));
|
assertThat(cb.toString(), is(json.toString()));
|
||||||
|
|
||||||
// Make sure the returned thumbprint is correct
|
// 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)));
|
assertThat(thumbprint, is(Base64Url.decode(TestUtils.THUMBPRINT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.security.KeyPair;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.jose4j.base64url.Base64Url;
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -88,4 +89,17 @@ public class SignatureUtilsTest {
|
||||||
assertThat(type, is("ES512"));
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue