Java Webhook ReceiverΒΆ
Download Webhook.java
/*
* Debugging webhook receiver endpoint.
*
* Example using Jetty 9.2 installed in $JETTY_HOME.
* http://www.eclipse.org/jetty/download.html
*
* $ java -jar $JETTY_HOME/start.jar --add-to-start=https --add-to-start=deploy --add-to-start=annotations
* $ mkdir -p webapps/webhook/WEB-INF/classes
* $ javac -d webapps/webhook/WEB-INF/classes -cp $JETTY_HOME/lib/servlet-api-3.1.jar WebhookServlet.java
* $ java -jar $JETTY_HOME/start.jar
*
* # curl using a signature derived using the default api key "SECRET"
* $ curl -k -v --header "X-Signature: 3q8QXTAGaey18yL8FWTqdVlbMr6hcuNvM4tefa0o9nA=" --data '{}' https://localhost:8443/webhook/sent
*
* You can configure webapps/webhook/WEB-INF/web.xml to specify a different "apiKey" init-param.
*/
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.xml.bind.DatatypeConverter;
@WebServlet(
urlPatterns={"/sent", "/read", "/status"},
initParams = {
@WebInitParam(name="apiKey", value="SECRET"),
})
public class WebhookServlet extends HttpServlet{
private final static Logger logger = Logger.getLogger(WebhookServlet.class.getName());
private String apiKey;
@Override
public void init(ServletConfig config) throws ServletException {
apiKey = config.getInitParameter("apiKey");
}
private int validateSignature(String signature, byte[] body) throws IOException, ServletException {
if (signature == null) {
logger.warning("No signature supplied, forbidden");
return HttpServletResponse.SC_FORBIDDEN;
}
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
sha256_HMAC.init(new SecretKeySpec(apiKey.getBytes(), "HmacSHA256"));
String computedSignature = DatatypeConverter.printBase64Binary(sha256_HMAC.doFinal(body));
if (!signature.equals(computedSignature)) {
logger.warning(String.format("Signature does not match, forbidden (%s != %s)", signature, computedSignature));
return HttpServletResponse.SC_FORBIDDEN;
}
logger.info("Signature validated");
return HttpServletResponse.SC_NO_CONTENT;
} catch (GeneralSecurityException e) {
throw new ServletException("HMAC failed", e);
}
}
private byte[] readFully(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[2048];
int read = 0;
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
}
return out.toByteArray();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
logger.info(String.format("Request: %s", request.getRequestURI()));
byte[] body = readFully(request.getInputStream());
logger.info(String.format("Body: %s", new String(body, "UTF-8")));
int statusCode = validateSignature(request.getHeader("X-Signature"), body);
response.setStatus(statusCode);
}
}