Skip to content

Commit 52cf193

Browse files
authored
Merge pull request #13 from FreeClimbAPI/VCSWP-14050
Vcswp-14050: Signing Secrets verification
2 parents e9c6ce4 + e3fe973 commit 52cf193

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
77
## [Unreleased]
88
None
99

10+
<a name="4.0.0"></a>
11+
## [4.0.0] - 2021-05-03
12+
### Added
13+
- Add `verifyRequest` utility function
14+
1015
<a name="3.0.3"></a>
1116
## [3.0.3] - 2020-12-16
1217
### Added

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ group = 'com.github.FreeClimbAPI'
99

1010
sourceCompatibility = 1.7 // java 7
1111
targetCompatibility = 1.7
12-
version = '3.0.3'
12+
version = '4.0.0'
1313

1414
repositories {
1515
mavenCentral()
@@ -23,6 +23,7 @@ dependencies {
2323
testCompile 'junit:junit:4.12'
2424

2525
compile 'com.google.code.gson:gson:2.6.2'
26+
compile 'commons-codec:commons-codec:1.15'
2627
}
2728

2829
task sourcesJar(type: Jar, dependsOn: classes) {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.vailsys.freeclimb;
2+
3+
import java.io.UnsupportedEncodingException;
4+
import java.net.URLDecoder;
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import java.util.ArrayList;
8+
import java.util.Date;
9+
10+
import javax.crypto.Mac;
11+
import javax.crypto.spec.SecretKeySpec;
12+
13+
import com.vailsys.freeclimb.api.FreeClimbException;
14+
import org.apache.commons.codec.binary.Hex;
15+
16+
public class Utils {
17+
/**
18+
* Verify a request's signature
19+
*
20+
* @param requestBody The request's body
21+
* @param signatureHeader The request's `freeclimb-signature` header
22+
* @param signingSecret A signing secret from the FreeClimb account
23+
* @param tolerance Acceptable duration threshold represented in
24+
* milliseconds, defaulting to 5 minutes when not
25+
* provided
26+
* @throws FreeClimbException upon failed verification
27+
*/
28+
public static void verifyRequest(String requestBody, String signatureHeader, String signingSecret, int tolerance) throws FreeClimbException {
29+
String[] values = signatureHeader.split(",");
30+
31+
long time = 0;
32+
List<String> v1 = new ArrayList<String>();
33+
for (String queryString : values) {
34+
try {
35+
final int idex =queryString.indexOf("=");
36+
String key = idex > 0
37+
? URLDecoder.decode(queryString.substring(0, idex), "UTF-8")
38+
: queryString;
39+
String value = (idex > 0) && (queryString.length() > idex + 1)
40+
? queryString.substring(idex + 1)
41+
: null;
42+
if (key.equals("t")) {
43+
time = Long.parseLong(value);
44+
}
45+
if (key.equals("v1")) {
46+
v1.add(value);
47+
}
48+
} catch (UnsupportedEncodingException e) {
49+
throw new FreeClimbException(e);
50+
}
51+
}
52+
53+
long currentTime = new Date().getTime();
54+
long signatureAge = currentTime - (time*1000);
55+
if (tolerance < signatureAge) {
56+
throw new FreeClimbException(String.format("Request rejected - signature's timestamp failed against current tolerance of %2d milliseconds. Signature age: %2d milliseconds", tolerance, signatureAge));
57+
}
58+
59+
String data = time + "." + requestBody;
60+
try {
61+
Mac mac = Mac.getInstance("HmacSHA256");
62+
mac.init(new SecretKeySpec(signingSecret.getBytes(), "HmacSHA256"));
63+
String hmac = Hex.encodeHexString(mac.doFinal(data.getBytes()));
64+
65+
if (!v1.contains(hmac)) {
66+
throw new FreeClimbException(String.format("Unverified Request Signature - FreeClimb was unable to verify that this request originated from FreeClimb. If this request was unexpected, it may be from a bad actor. Please proceed with caution. If this request was expected, to fix this issue try checking for any typos or misspelling of your signing secret."));
67+
}
68+
} catch (Exception e) {
69+
throw new FreeClimbException("Failed to calculate hmac using ", e);
70+
}
71+
}
72+
73+
/**
74+
* Verify a request's signature
75+
*
76+
* @param requestBody The request's body
77+
* @param signatureHeader The request's `freeclimb-signature` header
78+
* @param signingSecret A signing secret from the FreeClimb account
79+
* @throws FreeClimbException upon failed verification
80+
*/
81+
public static void verifyRequest(String requestBody, String signatureHeader, String signingSecret) throws FreeClimbException {
82+
Utils.verifyRequest(requestBody, signatureHeader, signingSecret, 5*60*1000);
83+
}
84+
}

0 commit comments

Comments
 (0)