1/**
2*
3* The utility method can be used to validate/verify the signed request.
4* In this case, the signed request is verified that it's from Salesforce and that
5* it has not been tampered with.
6*
7* This utility class has two methods. One verifies and decodes the request
8* as a Java object, the other as a JSON String.
9*
10*/
11public class SignedRequest {
12 public static CanvasRequest verifyAndDecode(String input, String secret)
13 throws SecurityException {
14 String[] split = getParts(input);
15 String encodedSig = split[0];
16 String encodedEnvelope = split[1];
17
18 // Deserialize the JSON body.
19 String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
20 ObjectMapper mapper = new ObjectMapper();
21 ObjectReader reader = mapper.reader(CanvasRequest.class);
22 CanvasRequest canvasRequest;
23 String algorithm;
24 try {
25 canvasRequest = reader.readValue(json_envelope);
26 algorithm = canvasRequest.getAlgorithm() == null ?
27 "HMACSHA256" : canvasRequest.getAlgorithm();
28 } catch (IOException e) {
29 throw new SecurityException(String.format("Error [%s] deserializing JSON to
30 Object [%s]", e.getMessage(), CanvasRequest.class.getName()), e);
31 }
32 verify(secret, algorithm, encodedEnvelope, encodedSig);
33 // If we got this far, then the request was not tampered with.
34 // Return the request as a Java object.
35 return canvasRequest;
36 }
37 public static String verifyAndDecodeAsJson(String input, String secret)
38 throws SecurityException {
39 String[] split = getParts(input);
40 String encodedSig = split[0];
41 String encodedEnvelope = split[1];
42 String json_envelope = new String(new Base64(true).decode(encodedEnvelope));
43 ObjectMapper mapper = new ObjectMapper();
44 String algorithm;
45 StringWriter writer;
46 TypeReference<HashMap<String,Object>> typeRef
47 = new TypeReference<HashMap<String, Object>>() { };
48 try {
49 HashMap<String,Object> o = mapper.readValue(json_envelope, typeRef);
50 writer = new StringWriter();
51 mapper.writeValue(writer, o);
52 algorithm = (String)o.get("algorithm");
53 } catch (IOException e) {
54 throw new SecurityException(String.format("Error [%s] deserializing
55 JSON to Object [%s]", e.getMessage(),
56 typeRef.getClass().getName()), e);
57 }
58 verify(secret, algorithm, encodedEnvelope, encodedSig);
59 // If we got this far, then the request was not tampered with.
60 // Return the request as a JSON string.
61 return writer.toString();
62 }
63
64 private static String[] getParts(String input) {
65 if (input == null || input.indexOf(".") <= 0) {
66 throw new SecurityException(String.format("Input [%s] doesn't
67 look like a signed request", input));
68 }
69 String[] split = input.split("[.]", 2);
70 return split;
71 }
72
73 private static void verify(String secret, String algorithm,
74 String encodedEnvelope, String encodedSig )
75 throws SecurityException
76 {
77 if (secret == null || secret.trim().length() == 0) {
78 throw new IllegalArgumentException("secret is null, did you
79 set your environment variable CANVAS_CONSUMER_SECRET?");
80 }
81 SecretKey hmacKey = null;
82 try {
83 byte[] key = secret.getBytes();
84 hmacKey = new SecretKeySpec(key, algorithm);
85 Mac mac = Mac.getInstance(algorithm);
86 mac.init(hmacKey);
87 // Check to see if the body was tampered with.
88 byte[] digest = mac.doFinal(encodedEnvelope.getBytes());
89 byte[] decode_sig = new Base64(true).decode(encodedSig);
90 if (! Arrays.equals(digest, decode_sig)) {
91 String label = "Warning: Request was tampered with";
92 throw new SecurityException(label);
93 }
94 } catch (NoSuchAlgorithmException e) {
95 throw new SecurityException(String.format("Problem with algorithm [%s]
96 Error [%s]", algorithm, e.getMessage()), e);
97 } catch (InvalidKeyException e) {
98 throw new SecurityException(String.format("Problem with key [%s]
99 Error [%s]", hmacKey, e.getMessage()), e);
100 }
101 // If we got here and didn't throw a SecurityException then all is good.
102 }
103}