package com.twilio.security; import org.apache.commons.codec.binary.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; public class RequestValidator { private static final String HMAC = "HmacSHA1"; private final SecretKeySpec signingKey; public RequestValidator(String authToken) { this.signingKey = new SecretKeySpec(authToken.getBytes(), HMAC); } public boolean validate(String url, Map<String, String> params, String expectedSignature) { String signature = getValidationSignature(url, params); return secureCompare(signature, expectedSignature); } private String getValidationSignature(String url, Map<String, String> params) { try { StringBuilder builder = new StringBuilder(url); if (params != null) { List<String> sortedKeys = new ArrayList<>(params.keySet()); Collections.sort(sortedKeys); for (String s : sortedKeys) { builder.append(s); String v = params.get(s); builder.append(v == null ? "" : v); } } Mac mac = Mac.getInstance(HMAC); mac.init(signingKey); byte[] rawHmac = mac.doFinal(builder.toString().getBytes(StandardCharsets.UTF_8)); return new String(Base64.encodeBase64(rawHmac)); } catch (Exception e) { return null; } } private boolean secureCompare(String a, String b) { if (a == null || b == null) { return false; } int n = a.length(); if (n != b.length()) { return false; } int mismatch = 0; for (int i = 0; i < n; ++i) { mismatch |= a.charAt(i) ^ b.charAt(i); } return mismatch == 0; } }