Last active
December 28, 2015 16:48
-
-
Save jamsesso/0eadef568a0e5e3f1137 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.gson.Gson; | |
import java.util.Date; | |
import lombok.Data; | |
import com.google.common.collect.Maps; | |
import java.util.Map; | |
/** | |
* The message class. | |
* This class should be present on both the client as well as the receiving app. | |
* You can serialize this class to send it over the network. JSON is a good choice. | |
*/ | |
@Data | |
public class Message { | |
private final long timestamp; | |
private final Object publicKey; | |
private final String type; | |
private final String payload; | |
private final String signature; | |
public Message(String type, Object publicKey, Object secretKey, Object payload) { | |
SignatureGenerator signatureGenerator = new SignatureGenerator(); | |
Gson gson = new Gson(); | |
this.timestamp = new Date().getTime() / 1000; | |
this.publicKey = publicKey; | |
this.type = type; | |
this.payload = gson.toJson(payload); | |
Map<String, String> parameters = Maps.newHashMap(); | |
parameters.put("payload", this.payload); | |
parameters.put("publicKey", publicKey.toString()); | |
parameters.put("timestamp", Long.toString(this.timestamp)); | |
parameters.put("type", type); | |
String digest = Util.createQueryString(parameters); | |
this.signature = signatureGenerator.generate(secretKey.toString(), digest); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.collect.Maps; | |
import java.util.Map; | |
import java.util.Optional; | |
/** | |
* The Message Authenticator is used on the receiving app side. | |
* When a message is received, pass it to this class to check that the generated signatures are equal. | |
*/ | |
public class MessageAuthenticator { | |
private final SignatureGenerator signatureGenerator; | |
public MessageAuthenticator(SignatureGenerator signatureGenerator) { | |
this.signatureGenerator = signatureGenerator; | |
} | |
public boolean authenticate(Message message) { | |
long publicKey = message.getPublicKey(); | |
Optional<String> maybeSecretKey = getSecretKeySomehow(publicKey); | |
if(!maybeSecretKey.isPresent()) { | |
// Couldn't find a secret key with that public key. | |
return false; | |
} | |
Map<String, String> parameters = Maps.newHashMap(); | |
parameters.put("payload", message.getPayload()); | |
parameters.put("publicKey", Long.toString(message.getPublicKey())); | |
parameters.put("timestamp", Long.toString(message.getTimestamp())); | |
parameters.put("type", message.getType()); | |
String digest = Util.createQueryString(parameters); | |
String secretKey = maybeSecretKey.get(); | |
String signature = signatureGenerator.generate(secretKey, digest); | |
return signature.equalsIgnoreCase(message.getSignature()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.nio.charset.Charset; | |
import java.security.InvalidKeyException; | |
import java.security.Key; | |
import java.security.NoSuchAlgorithmException; | |
import javax.crypto.Mac; | |
import javax.crypto.spec.SecretKeySpec; | |
import lombok.extern.slf4j.Slf4j; | |
/** | |
* A signature generator class. | |
* This class should also be present on both the client and receiving app. | |
*/ | |
@Slf4j | |
public class SignatureGenerator { | |
public static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); | |
public String generate(String key, String message) { | |
String signature = ""; | |
Charset encoding = Charset.forName("UTF-8"); | |
try { | |
Mac mac = Mac.getInstance("HMACSHA256"); | |
Key symmetricKey = new SecretKeySpec(key.getBytes(encoding), "HMACSHA256"); | |
mac.init(symmetricKey); | |
signature = bytesToHex(mac.doFinal(message.getBytes(encoding))); | |
} | |
catch(NoSuchAlgorithmException e) { | |
log.error("No HMAC SHA256 algorithm available for generating MAC", e); | |
} | |
catch(InvalidKeyException e) { | |
log.error("Invalid key passed to Sha256 signature service: {}", key, e); | |
} | |
return signature; | |
} | |
private static String bytesToHex(byte[] bytes) { | |
char[] hexChars = new char[bytes.length * 2]; | |
for(int j = 0; j < bytes.length; j++) { | |
int v = bytes[j] & 0xFF; | |
hexChars[j * 2] = HEX_ARRAY[v >>> 4]; | |
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; | |
} | |
return new String(hexChars); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Map; | |
import java.io.UnsupportedEncodingException; | |
import static java.net.URLEncoder.encode; | |
import static java.util.Map.Entry.comparingByKey; | |
/** | |
* The Util class can be used on the client and server to help format signatures. | |
*/ | |
public class Util { | |
public static final String URL_ENCODING = "UTF-8"; | |
public static String createQueryString(Map<String, String> parameters) { | |
return parameters.entrySet().stream() | |
.filter(p -> p.getKey() != null && p.getValue() != null) | |
.sorted(comparingByKey()) | |
.map(p -> urlEncode(p.getKey()) + "=" + urlEncode(p.getValue())) | |
.reduce((left, right) -> left + "&" + right) | |
.orElse(""); | |
} | |
public static String urlEncode(String raw) { | |
try { | |
return encode(raw, URL_ENCODING); | |
} | |
catch(UnsupportedEncodingException e) { | |
throw new UnsupportedOperationException(e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment