mirror of https://github.com/shred/acme4j
Use jose4j's thumbprint calculation
parent
31c4d6d133
commit
91c402473f
|
@ -290,11 +290,12 @@ public class Registration extends AcmeResource {
|
||||||
|
|
||||||
String rollover;
|
String rollover;
|
||||||
try {
|
try {
|
||||||
|
final PublicJsonWebKey oldKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(getSession().getKeyPair().getPublic());
|
||||||
|
final PublicJsonWebKey newKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(newKeyPair.getPublic());
|
||||||
|
|
||||||
ClaimBuilder newKeyClaim = new ClaimBuilder();
|
ClaimBuilder newKeyClaim = new ClaimBuilder();
|
||||||
newKeyClaim.putResource("reg");
|
newKeyClaim.putResource("reg");
|
||||||
newKeyClaim.putBase64("newKey", SignatureUtils.jwkThumbprint(newKeyPair.getPublic()));
|
newKeyClaim.putBase64("newKey", newKeyJwk.calculateThumbprint("SHA-256"));
|
||||||
|
|
||||||
final PublicJsonWebKey oldKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(getSession().getKeyPair().getPublic());
|
|
||||||
|
|
||||||
JsonWebSignature jws = new JsonWebSignature();
|
JsonWebSignature jws = new JsonWebSignature();
|
||||||
jws.setPayload(newKeyClaim.toString());
|
jws.setPayload(newKeyClaim.toString());
|
||||||
|
|
|
@ -16,10 +16,11 @@ package org.shredzone.acme4j.challenge;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
import org.jose4j.base64url.Base64Url;
|
import org.jose4j.base64url.Base64Url;
|
||||||
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An extension of {@link Challenge} that handles challenges with a {@code token} and
|
* An extension of {@link Challenge} that handles challenges with a {@code token} and
|
||||||
|
@ -77,10 +78,15 @@ public class TokenChallenge extends Challenge {
|
||||||
* @return Authorization string
|
* @return Authorization string
|
||||||
*/
|
*/
|
||||||
protected String computeAuthorization() {
|
protected String computeAuthorization() {
|
||||||
|
try {
|
||||||
PublicKey pk = getSession().getKeyPair().getPublic();
|
PublicKey pk = getSession().getKeyPair().getPublic();
|
||||||
|
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
|
||||||
return getToken()
|
return getToken()
|
||||||
+ '.'
|
+ '.'
|
||||||
+ Base64Url.encode(SignatureUtils.jwkThumbprint(pk));
|
+ Base64Url.encode(jwk.calculateThumbprint("SHA-256"));
|
||||||
|
} catch (JoseException ex) {
|
||||||
|
throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,19 +13,11 @@
|
||||||
*/
|
*/
|
||||||
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.
|
||||||
|
@ -73,32 +65,4 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,14 +26,10 @@ import java.io.ObjectOutputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jose4j.base64url.Base64Url;
|
|
||||||
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.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -43,7 +39,6 @@ import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
|
||||||
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
||||||
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;
|
||||||
|
|
||||||
|
@ -140,32 +135,6 @@ public class ChallengeTest {
|
||||||
challenge.unmarshall(TestUtils.getJsonAsMap("dnsChallenge"));
|
challenge.unmarshall(TestUtils.getJsonAsMap("dnsChallenge"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that the test keypair's thumbprint is correct.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testJwkThumbprint() throws IOException, JoseException {
|
|
||||||
StringBuilder json = new StringBuilder();
|
|
||||||
json.append('{');
|
|
||||||
json.append("\"e\":\"").append(TestUtils.E).append("\",");
|
|
||||||
json.append("\"kty\":\"").append(TestUtils.KTY).append("\",");
|
|
||||||
json.append("\"n\":\"").append(TestUtils.N).append("\"");
|
|
||||||
json.append('}');
|
|
||||||
|
|
||||||
KeyPair keypair = TestUtils.createKeyPair();
|
|
||||||
|
|
||||||
// Test the JWK raw output. The JSON string must match the assert string
|
|
||||||
// exactly, as the thumbprint is a digest of that string.
|
|
||||||
final JsonWebKey jwk = JsonWebKey.Factory.newJwk(keypair.getPublic());
|
|
||||||
ClaimBuilder cb = new ClaimBuilder();
|
|
||||||
cb.putAll(jwk.toParams(OutputControlLevel.PUBLIC_ONLY));
|
|
||||||
assertThat(cb.toString(), is(json.toString()));
|
|
||||||
|
|
||||||
// Make sure the returned thumbprint is correct
|
|
||||||
byte[] thumbprint = SignatureUtils.jwkThumbprint(keypair.getPublic());
|
|
||||||
assertThat(thumbprint, is(Base64Url.decode(TestUtils.THUMBPRINT)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that a challenge can be triggered.
|
* Test that a challenge can be triggered.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,7 +20,6 @@ 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;
|
||||||
|
@ -87,17 +86,4 @@ 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