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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||
|
||||
/**
|
||||
* Implements the {@value TYPE} challenge.
|
||||
|
@ -47,14 +43,7 @@ public class Dns01Challenge extends TokenChallenge {
|
|||
* record.
|
||||
*/
|
||||
public String getDigest() {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
return base64UrlEncode(sha256hash(getAuthorization()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -13,12 +13,9 @@
|
|||
*/
|
||||
package org.shredzone.acme4j.challenge;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||
|
||||
/**
|
||||
* Implements the {@value TYPE} challenge.
|
||||
|
@ -30,7 +27,6 @@ import org.shredzone.acme4j.exception.AcmeProtocolException;
|
|||
@Deprecated
|
||||
public class TlsSni01Challenge extends TokenChallenge {
|
||||
private static final long serialVersionUID = 7370329525205430573L;
|
||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
||||
|
||||
/**
|
||||
* Challenge type name: {@value}
|
||||
|
@ -65,32 +61,8 @@ public class TlsSni01Challenge extends TokenChallenge {
|
|||
protected void authorize() {
|
||||
super.authorize();
|
||||
|
||||
String hash = computeHash(getAuthorization());
|
||||
String hash = hexEncode(sha256hash(getAuthorization()));
|
||||
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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import static org.shredzone.acme4j.util.AcmeUtils.*;
|
||||
|
||||
import org.shredzone.acme4j.Session;
|
||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||
|
||||
/**
|
||||
* Implements the {@value TYPE} challenge.
|
||||
*/
|
||||
public class TlsSni02Challenge extends TokenChallenge {
|
||||
private static final long serialVersionUID = 8921833167878544518L;
|
||||
private static final char[] HEX = "0123456789abcdef".toCharArray();
|
||||
|
||||
/**
|
||||
* Challenge type name: {@value}
|
||||
|
@ -70,35 +66,11 @@ public class TlsSni02Challenge extends TokenChallenge {
|
|||
protected void authorize() {
|
||||
super.authorize();
|
||||
|
||||
String tokenHash = computeHash(getToken());
|
||||
String tokenHash = hexEncode(sha256hash(getToken()));
|
||||
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";
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode;
|
||||
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.jose4j.jwk.PublicJsonWebKey;
|
||||
import org.jose4j.lang.JoseException;
|
||||
import org.shredzone.acme4j.Session;
|
||||
|
@ -83,7 +84,7 @@ public class TokenChallenge extends Challenge {
|
|||
PublicJsonWebKey jwk = PublicJsonWebKey.Factory.newPublicJwk(pk);
|
||||
return getToken()
|
||||
+ '.'
|
||||
+ Base64Url.encode(jwk.calculateThumbprint("SHA-256"));
|
||||
+ base64UrlEncode(jwk.calculateThumbprint("SHA-256"));
|
||||
} catch (JoseException 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;
|
||||
|
||||
import static org.shredzone.acme4j.util.AcmeUtils.base64UrlEncode;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.PublicKey;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -22,7 +24,6 @@ import java.util.Map;
|
|||
import java.util.TimeZone;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.jose4j.base64url.Base64Url;
|
||||
import org.jose4j.json.JsonUtil;
|
||||
import org.jose4j.jwk.JsonWebKey;
|
||||
import org.jose4j.jwk.PublicJsonWebKey;
|
||||
|
@ -130,7 +131,7 @@ public class ClaimBuilder {
|
|||
* @return {@code this}
|
||||
*/
|
||||
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