com.pusher.rest.SignatureUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pusher-http-java Show documentation
Show all versions of pusher-http-java Show documentation
This is a Java library for interacting with Pusher.com's HTTP API
package com.pusher.rest;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.client.utils.URIBuilder;
import com.pusher.rest.util.Prerequisites;
public class SignatureUtil {
public static URI uri(final String method,
final String scheme,
final String host,
final String path,
final String body,
final String key,
final String secret,
final Map extraParams) {
Prerequisites.noReservedKeys(extraParams);
try {
final Map allParams = new HashMap(extraParams);
allParams.put("auth_key", key);
allParams.put("auth_version", "1.0");
allParams.put("auth_timestamp", new Long(System.currentTimeMillis() / 1000).toString());
if (body != null) {
allParams.put("body_md5", bodyMd5(body));
}
// This is where the auth gets a bit weird. The query params for the request must include
// the auth signature which is a signature over all the params except itself.
allParams.put("auth_signature", sign(buildSignatureString(method, path, allParams), secret));
final URIBuilder b = new URIBuilder()
.setScheme(scheme)
.setHost(host)
.setPath(path);
for (final Entry e : allParams.entrySet()) {
b.setParameter(e.getKey(), e.getValue());
}
return b.build();
}
catch (final URISyntaxException e) {
throw new RuntimeException("Could not build URI", e);
}
}
private static String bodyMd5(final String body) {
try {
final MessageDigest md = MessageDigest.getInstance("MD5");
final byte[] digest = md.digest(body.getBytes("UTF-8"));
return Hex.encodeHexString(digest);
}
// If this doesn't exist, we're pretty much out of luck.
catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("The Pusher HTTP client requires MD5 support", e);
}
catch (final UnsupportedEncodingException e) {
throw new RuntimeException("The Pusher HTTP client needs UTF-8 support", e);
}
}
public static String sign(final String input, final String secret) {
try {
final Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(), "SHA256"));
final byte[] digest = mac.doFinal(input.getBytes("UTF-8"));
return Hex.encodeHexString(digest);
}
catch (final InvalidKeyException e) {
/// We validate this when the key is first provided, so we should never encounter it here.
throw new RuntimeException("Invalid secret key", e);
}
// If either of these doesn't exist, we're pretty much out of luck.
catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("The Pusher HTTP client requires HmacSHA256 support", e);
}
catch (final UnsupportedEncodingException e) {
throw new RuntimeException("The Pusher HTTP client needs UTF-8 support", e);
}
}
// Visible for testing
static String buildSignatureString(final String method, final String path, final Map queryParams) {
final StringBuilder sb = new StringBuilder();
sb.append(method)
.append('\n')
.append(path)
.append('\n');
final String[] keys = queryParams.keySet().toArray(new String[0]);
Arrays.sort(keys);
boolean first = true;
for (final String key : keys) {
if (!first) sb.append('&');
else first = false;
sb.append(key)
.append('=')
.append(queryParams.get(key));
}
return sb.toString();
}
}