Skip to content

Commit c0a3e62

Browse files
committed
Add RequestValidator
1 parent d6e984e commit c0a3e62

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.twilio.security;
2+
3+
import org.apache.commons.codec.binary.Base64;
4+
5+
import javax.crypto.Mac;
6+
import javax.crypto.spec.SecretKeySpec;
7+
import java.nio.charset.StandardCharsets;
8+
import java.util.ArrayList;
9+
import java.util.Collections;
10+
import java.util.List;
11+
import java.util.Map;
12+
13+
public class RequestValidator {
14+
15+
private static final String HMAC = "HmacSHA1";
16+
17+
private final SecretKeySpec signingKey;
18+
19+
public RequestValidator(String authToken) {
20+
this.signingKey = new SecretKeySpec(authToken.getBytes(), HMAC);
21+
}
22+
23+
public boolean validate(String url, Map<String, String> params, String expectedSignature) {
24+
String signature = getValidationSignature(url, params);
25+
return secureCompare(signature, expectedSignature);
26+
}
27+
28+
private String getValidationSignature(String url, Map<String, String> params) {
29+
try {
30+
31+
StringBuilder builder = new StringBuilder(url);
32+
if (params != null) {
33+
List<String> sortedKeys = new ArrayList<>(params.keySet());
34+
Collections.sort(sortedKeys);
35+
36+
for (String s : sortedKeys) {
37+
builder.append(s);
38+
39+
String v = params.get(s);
40+
builder.append(v == null ? "" : v);
41+
}
42+
}
43+
44+
Mac mac = Mac.getInstance(HMAC);
45+
mac.init(signingKey);
46+
47+
byte[] rawHmac = mac.doFinal(builder.toString().getBytes(StandardCharsets.UTF_8));
48+
return new String(Base64.encodeBase64(rawHmac));
49+
50+
} catch (Exception e) {
51+
return null;
52+
}
53+
}
54+
55+
private boolean secureCompare(String a, String b) {
56+
if (a == null || b == null) {
57+
return false;
58+
}
59+
60+
int n = a.length();
61+
if (n != b.length()) {
62+
return false;
63+
}
64+
65+
int mismatch = 0;
66+
for (int i = 0; i < n; ++i) {
67+
mismatch |= a.charAt(i) ^ b.charAt(i);
68+
}
69+
return mismatch == 0;
70+
}
71+
72+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.twilio.security;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
/**
10+
* Test class for {@link RequestValidator}.
11+
*/
12+
public class RequestValidatorTest {
13+
14+
@Test
15+
public void testValidate() {
16+
17+
RequestValidator validator = new RequestValidator("12345");
18+
19+
String url = "https://mycompany.com/myapp.php?foo=1&bar=2";
20+
Map<String, String> params = new HashMap<>();
21+
params.put("CallSid", "CA1234567890ABCDE");
22+
params.put("Caller", "+14158675309");
23+
params.put("Digits", "1234");
24+
params.put("From", "+14158675309");
25+
params.put("To", "+18005551212");
26+
27+
String signature = "RSOYDt4T1cUTdK1PDd93/VVr8B8=";
28+
29+
Assert.assertTrue("Request does not match provided signature", validator.validate(url, params, signature));
30+
}
31+
32+
}

0 commit comments

Comments
 (0)