Created
March 19, 2019 00:19
-
-
Save a-shevtsov/bff675a1b03640ede6f7c02920e160a6 to your computer and use it in GitHub Desktop.
Signing requests with AWS Sigv4 in JMeter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This example has been tested against API Gateway | |
* The following HTTP Headers must be set (in HTTP Header Manager) | |
* Host: <hostname> | |
* Content-Type: application/json | |
* x-amz-date: ${amzDate} | |
* Authorization: ${amzAuth} | |
*/ | |
import java.nio.charset.StandardCharsets; | |
import java.security.MessageDigest; | |
import java.time.LocalDateTime; | |
import java.time.ZoneOffset; | |
import javax.crypto.Mac; | |
import javax.crypto.spec.SecretKeySpec; | |
final static char[] hexArray = "0123456789abcdef".toCharArray(); | |
String getAmazonDate(LocalDateTime dt) { | |
String amzDate = dt.getYear().toString(); | |
amzDate += (dt.getMonthValue() < 10) ? "0" + dt.getMonthValue() : dt.getMonthValue(); | |
amzDate += (dt.getDayOfMonth() < 10) ? "0" + dt.getDayOfMonth() : dt.getDayOfMonth(); | |
amzDate += "T"; | |
amzDate += (dt.getHour() < 10) ? "0" + dt.getHour() : dt.getHour(); | |
amzDate += (dt.getMinute() < 10) ? "0" + dt.getMinute() : dt.getMinute(); | |
amzDate += (dt.getSecond() < 10) ? "0" + dt.getSecond() : dt.getSecond(); | |
amzDate += "Z"; | |
return amzDate; | |
} | |
byte[] HmacSHA256(String data, byte[] key) { | |
String algorithm = "HmacSHA256"; | |
Mac mac = Mac.getInstance(algorithm); | |
mac.init(new SecretKeySpec(key, algorithm)); | |
return mac.doFinal(data.getBytes("UTF-8")); | |
} | |
byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) { | |
byte[] kSecret = ("AWS4" + key).getBytes("UTF-8"); | |
byte[] kDate = HmacSHA256(dateStamp, kSecret); | |
byte[] kRegion = HmacSHA256(regionName, kDate); | |
byte[] kService = HmacSHA256(serviceName, kRegion); | |
byte[] kSigning = HmacSHA256("aws4_request", kService); | |
return kSigning; | |
} | |
String bytesToHex(byte[] bytes) { | |
char[] hexChars = new char[bytes.length * 2]; | |
for ( int j = 0; j < bytes.length; j++ ) { | |
int v = bytes[j] & 0xFF; | |
hexChars[j * 2] = hexArray[v >>> 4]; | |
hexChars[j * 2 + 1] = hexArray[v & 0x0F]; | |
} | |
return new String(hexChars); | |
} | |
byte[] getSha256(String text) { | |
MessageDigest digest = MessageDigest.getInstance("SHA-256"); | |
byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8)); | |
return hash; | |
} | |
// Retrieve method | |
String method = sampler.getMethod(); | |
log.info("Method: " + method); | |
// Retrieve body | |
String body = method.equalsIgnoreCase("POST") ? sampler.getArguments().getArgument(0).getValue() : ""; | |
log.info("Body: " + body); | |
// Retrieve URL from HTTP Request | |
URL url = sampler.getUrl(); | |
String host = url.getHost(); | |
log.info("Host: " + host); | |
String canonicalUri = url.getPath(); | |
log.info("Path: " + canonicalUri); | |
String apiKey = "..access_key.."; | |
String secretKey = "..secret_key.."; | |
String region = "..region.."; # us-west-2 / us-east-1 / ... | |
String serviceName = "execute-api"; | |
String canonicalQuerystring = ""; | |
LocalDateTime currentTime = LocalDateTime.now(ZoneOffset.UTC); | |
String amzDate = getAmazonDate(currentTime); | |
String datestamp = amzDate.substring(0, 8); | |
String canonicalHeaders = "content-type:application/json\n" + "host:" + host + "\n" + "x-amz-date:" + amzDate + "\n"; | |
String signedHeaders = "content-type;host;x-amz-date"; | |
String payloadHash = bytesToHex(getSha256(body)); | |
String canonicalRequest = method + "\n" + canonicalUri + "\n" + canonicalQuerystring + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + payloadHash; | |
log.info("Canonical request: " + canonicalRequest); | |
String algorithm = "AWS4-HMAC-SHA256"; | |
String credentialScope = datestamp + "/" + region + "/" + serviceName + "/aws4_request"; | |
String stringToSign = algorithm + "\n" + amzDate + "\n" + credentialScope + "\n" + bytesToHex(getSha256(canonicalRequest)); | |
log.info("String to sign: " + stringToSign); | |
byte[] signingKey = getSignatureKey(secretKey, datestamp, region, serviceName); | |
String signature = bytesToHex(HmacSHA256(stringToSign, signingKey)); | |
String amzAuth = "AWS4-HMAC-SHA256 Credential=" + apiKey + "/" + credentialScope + ", SignedHeaders=" + signedHeaders + ", Signature=" + signature; | |
log.info("Authorization: " + amzAuth); | |
vars.put("amzDate", amzDate); | |
vars.put("amzAuth", amzAuth); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment