mirror of https://github.com/shred/acme4j
Add AcmeUtils for commonly used functions
parent
53712df034
commit
1cca9e26af
|
@ -13,13 +13,9 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.challenge;
|
package org.shredzone.acme4j.challenge;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.jose4j.base64url.Base64Url;
|
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@value TYPE} challenge.
|
* Implements the {@value TYPE} challenge.
|
||||||
|
@ -47,14 +43,7 @@ public class Dns01Challenge extends TokenChallenge {
|
||||||
* record.
|
* record.
|
||||||
*/
|
*/
|
||||||
public String getDigest() {
|
public String getDigest() {
|
||||||
try {
|
return base64UrlEncode(sha256hash(getAuthorization()));
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
md.update(getAuthorization().getBytes("UTF-8"));
|
|
||||||
byte[] digest = md.digest();
|
|
||||||
return Base64Url.encode(digest);
|
|
||||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
|
|
||||||
throw new AcmeProtocolException("Failed to compute digest", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,12 +13,9 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.challenge;
|
package org.shredzone.acme4j.challenge;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@value TYPE} challenge.
|
* Implements the {@value TYPE} challenge.
|
||||||
|
@ -30,7 +27,6 @@ import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public class TlsSni01Challenge extends TokenChallenge {
|
public class TlsSni01Challenge extends TokenChallenge {
|
||||||
private static final long serialVersionUID = 7370329525205430573L;
|
private static final long serialVersionUID = 7370329525205430573L;
|
||||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Challenge type name: {@value}
|
* Challenge type name: {@value}
|
||||||
|
@ -65,32 +61,8 @@ public class TlsSni01Challenge extends TokenChallenge {
|
||||||
protected void authorize() {
|
protected void authorize() {
|
||||||
super.authorize();
|
super.authorize();
|
||||||
|
|
||||||
String hash = computeHash(getAuthorization());
|
String hash = hexEncode(sha256hash(getAuthorization()));
|
||||||
subject = hash.substring(0, 32) + '.' + hash.substring(32) + ".acme.invalid";
|
subject = hash.substring(0, 32) + '.' + hash.substring(32) + ".acme.invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes a hash according to the specifications.
|
|
||||||
*
|
|
||||||
* @param z
|
|
||||||
* Value to be hashed
|
|
||||||
* @return Hash
|
|
||||||
*/
|
|
||||||
private String computeHash(String z) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
md.update(z.getBytes("UTF-8"));
|
|
||||||
byte[] raw = md.digest();
|
|
||||||
char[] result = new char[raw.length * 2];
|
|
||||||
for (int ix = 0; ix < raw.length; ix++) {
|
|
||||||
int val = raw[ix] & 0xFF;
|
|
||||||
result[ix * 2] = HEX[val >>> 4];
|
|
||||||
result[ix * 2 + 1] = HEX[val & 0x0F];
|
|
||||||
}
|
|
||||||
return new String(result);
|
|
||||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
|
|
||||||
throw new AcmeProtocolException("Could not compute hash", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,19 +13,15 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.challenge;
|
package org.shredzone.acme4j.challenge;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the {@value TYPE} challenge.
|
* Implements the {@value TYPE} challenge.
|
||||||
*/
|
*/
|
||||||
public class TlsSni02Challenge extends TokenChallenge {
|
public class TlsSni02Challenge extends TokenChallenge {
|
||||||
private static final long serialVersionUID = 8921833167878544518L;
|
private static final long serialVersionUID = 8921833167878544518L;
|
||||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Challenge type name: {@value}
|
* Challenge type name: {@value}
|
||||||
|
@ -70,35 +66,11 @@ public class TlsSni02Challenge extends TokenChallenge {
|
||||||
protected void authorize() {
|
protected void authorize() {
|
||||||
super.authorize();
|
super.authorize();
|
||||||
|
|
||||||
String tokenHash = computeHash(getToken());
|
String tokenHash = hexEncode(sha256hash(getToken()));
|
||||||
subject = tokenHash.substring(0, 32) + '.' + tokenHash.substring(32) + ".token.acme.invalid";
|
subject = tokenHash.substring(0, 32) + '.' + tokenHash.substring(32) + ".token.acme.invalid";
|
||||||
|
|
||||||
String kaHash = computeHash(getAuthorization());
|
String kaHash = hexEncode(sha256hash(getAuthorization()));
|
||||||
sanB = kaHash.substring(0, 32) + '.' + kaHash.substring(32) + ".ka.acme.invalid";
|
sanB = kaHash.substring(0, 32) + '.' + kaHash.substring(32) + ".ka.acme.invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes a hash according to the specifications.
|
|
||||||
*
|
|
||||||
* @param z
|
|
||||||
* Value to be hashed
|
|
||||||
* @return Hash
|
|
||||||
*/
|
|
||||||
private String computeHash(String z) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
|
||||||
md.update(z.getBytes("UTF-8"));
|
|
||||||
byte[] raw = md.digest();
|
|
||||||
char[] result = new char[raw.length * 2];
|
|
||||||
for (int ix = 0; ix < raw.length; ix++) {
|
|
||||||
int val = raw[ix] & 0xFF;
|
|
||||||
result[ix * 2] = HEX[val >>> 4];
|
|
||||||
result[ix * 2 + 1] = HEX[val & 0x0F];
|
|
||||||
}
|
|
||||||
return new String(result);
|
|
||||||
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
|
|
||||||
throw new AcmeProtocolException("Could not compute hash", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,10 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.challenge;
|
package org.shredzone.acme4j.challenge;
|
||||||
|
|
||||||
|
import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
|
||||||
import org.jose4j.base64url.Base64Url;
|
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
import org.jose4j.lang.JoseException;
|
import org.jose4j.lang.JoseException;
|
||||||
import org.shredzone.acme4j.Session;
|
import org.shredzone.acme4j.Session;
|
||||||
|
@ -83,7 +84,7 @@ public class TokenChallenge extends Challenge {
|
||||||
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
|
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
|
||||||
return getToken()
|
return getToken()
|
||||||
+ '.'
|
+ '.'
|
||||||
+ Base64Url.encode(jwk.calculateThumbprint("SHA-256"));
|
+ base64UrlEncode(jwk.calculateThumbprint("SHA-256"));
|
||||||
} catch (JoseException ex) {
|
} catch (JoseException ex) {
|
||||||
throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
|
throw new AcmeProtocolException("Cannot compute key thumbprint", ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import org.jose4j.base64url.Base64Url;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains utility methods that are frequently used for the ACME protocol.
|
||||||
|
* <p>
|
||||||
|
* This class is internal. You may use it in your own code, but be warned that methods may
|
||||||
|
* change their signature or disappear without prior announcement.
|
||||||
|
*/
|
||||||
|
public final class AcmeUtils {
|
||||||
|
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
||||||
|
|
||||||
|
private AcmeUtils() {
|
||||||
|
// Utility class without constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a SHA-256 hash of the given string.
|
||||||
|
*
|
||||||
|
* @param z
|
||||||
|
* String to hash
|
||||||
|
* @return Hash
|
||||||
|
*/
|
||||||
|
public static byte[] sha256hash(String z) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||||
|
md.update(z.getBytes("UTF-8"));
|
||||||
|
return md.digest();
|
||||||
|
} catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
|
||||||
|
throw new AcmeProtocolException("Could not compute hash", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hex encodes the given byte array.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* byte array to hex encode
|
||||||
|
* @return Hex encoded string of the data (with lower case characters)
|
||||||
|
*/
|
||||||
|
public static String hexEncode(byte[] data) {
|
||||||
|
char[] result = new char[data.length * 2];
|
||||||
|
for (int ix = 0; ix < data.length; ix++) {
|
||||||
|
int val = data[ix] & 0xFF;
|
||||||
|
result[ix * 2] = HEX[val >>> 4];
|
||||||
|
result[ix * 2 + 1] = HEX[val & 0x0F];
|
||||||
|
}
|
||||||
|
return new String(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base64 encodes the given byte array, using URL style encoding.
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* byte array to base64 encode
|
||||||
|
* @return base64 encoded string
|
||||||
|
*/
|
||||||
|
public static String base64UrlEncode(byte[] data) {
|
||||||
|
return Base64Url.encode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,6 +13,8 @@
|
||||||
*/
|
*/
|
||||||
package org.shredzone.acme4j.util;
|
package org.shredzone.acme4j.util;
|
||||||
|
|
||||||
|
import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode;
|
||||||
|
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
@ -22,7 +24,6 @@ import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
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;
|
||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
|
@ -130,7 +131,7 @@ public class ClaimBuilder {
|
||||||
* @return {@code this}
|
* @return {@code this}
|
||||||
*/
|
*/
|
||||||
public ClaimBuilder putBase64(String key, byte[] data) {
|
public ClaimBuilder putBase64(String key, byte[] data) {
|
||||||
return put(key, Base64Url.encode(data));
|
return put(key, base64UrlEncode(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 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.util;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link AcmeUtils}.
|
||||||
|
*/
|
||||||
|
public class AcmeUtilsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test sha-256 hash.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSha256Hash() {
|
||||||
|
byte[] hash = sha256hash("foobar");
|
||||||
|
byte[] expected = DatatypeConverter.parseHexBinary("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2");
|
||||||
|
assertThat(hash, is(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test hex encode.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testHexEncode() {
|
||||||
|
String hexEncode = hexEncode(sha256hash("foobar"));
|
||||||
|
assertThat(hexEncode, is("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test base64 URL encode.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBase64UrlEncode() {
|
||||||
|
String base64UrlEncode = base64UrlEncode(sha256hash("foobar"));
|
||||||
|
assertThat(base64UrlEncode, is("w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue