I'm testing an application which is hosted on AWS infrastructure using JMeter tool wherein each request triggered needs to have a AWS signed header passed along with the request to validate the request at the IAM level in AWS. I have the access and secret key pertaining to the user role created in AWS console.
Is there any beanshell code available in JMeter which helps in generating AWS signature using the access and secret key for each request in JMeter?
The code is available on AWS docs website, see Deriving the Signing Key with Java for instance. Here is minimal listing just in case:
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Be aware that starting from JMeter version 3.1 it is recommended to use JSR223 Elements and Groovy language for scripting as it is more modern (supports all new Java SDK features), has a lot of "syntax sugar" on top of "normal" Java SDK and Groovy performance is much better comparing to Beanshell, JavaScript and other available options. See Apache Groovy - Why and How You Should Use It article for more details.
Related
Here is my code in the JSR223 PreProcessor.
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.security.InvalidKeyException
import java.security.MessageDigest
import groovy.json.JsonSlurper
import java.text.SimpleDateFormat
static byte[] HmacSHA256(String data, byte[] key) throws Exception {
String algorithm="HmacSHA256";
Mac mac = Mac.getInstance(algorithm);
mac.init(new SecretKeySpec(key, algorithm));
return mac.doFinal(data.getBytes("UTF8"));
}
static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception {
byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
Example parameter values to the function getSignatureKey are (these I am passing to the Parameters section of the JSR223 PreProcessor as variables)
key = eC6hEyRSTXMzsG6+juOObz8LbXb36iEYW7PPN1MJ
dateStamp = 20190123T083434Z
regionName = us-west-2
serviceName = test-mlp-us-west-2-4023179c-7708-4c5e-a831-28259b8a8872.s3.us-west-2.amazonaws.com
This code is not working and not generating the AWS signature.
Here is the sample Signature value I need to get
Signature=2a6092ec4ff49dc9j3b92d436635a57f312753kcc9f553ce1718b9b1594c4362
1.What is wrong with this code?
2.How can I assign the AWS signature to a variable and use in the JMeter?
If "This code is not working" the first thing you should do is to look into jmeter.log file - in case of Groovy script failure it will contain information regarding what exactly failed and why and some hints to fix.
I also think you should generate the signature basing on request details (URL, parameters, headers, etc.)
StringToSign =
Algorithm + \n +
RequestDateTime + \n +
CredentialScope + \n +
HashedCanonicalRequest
See Create a String to Sign for Signature Version 4
The whole process with examples is described in the How to Handle Dynamic AWS SigV4 in JMeter for API Testing and the test plan you can use as the reference is available in the GitHub repo.
I have seen pre-signed URL for S3 object. Is it possible to create pre-signed URL for API gateway. I have gone through documentation. I am using .NET. I would like to know if there is .NET library available to create pre-signed request for gateway API.
ISSUE
I have GET API something like this https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/pets?type=dog&page=1 and our client is going to invoke that API once in a while. The legacy tool that they are using only supports GET. So i wanted to create a pre-signed URL (with short expiry time) and give them when they ask for it. For each client i already have IAM user with their respective accesskey and secretkey
PreSigned URLs are typically signed with AWS SigV4 signing process.
You can generate SigV4 signed Urls for your API Gateway Hosted Endpoints. Typically, you will need to send SigV4 signature in Authorization Request Header. If you are clients are willing to send header, here is one sample Library you can try for .NET which creates a HTTP Request with signed header.
If your clients cannot send Authorization Header or cannot use above library then you can convert the signature to be a Query String Format and provide the pre-signed Urls to them.
This AWS Documentation has example in Python on how to generate Query String URL. Now, you can take python example and convert into .NET based code with following sample.
public string GetSig4QueryString(string host, string service, string region)
{
var t = DateTimeOffset.UtcNow;
var amzdate = t.ToString("yyyyMMddTHHmmssZ");
var datestamp = t.ToString("yyyyMMdd");
var canonical_uri = "/dev/myApigNodeJS";
var canonical_headers = "host:" + host+"\n";
var signed_headers = "host";
var credential_scope = $"{datestamp}/{region}/{service}/aws4_request";
var canonical_querystring = "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=" + WebUtility.UrlEncode(_access_key + "/" + credential_scope)
+ "&X-Amz-Date=" + amzdate + "&X-Amz-SignedHeaders=" + signed_headers;
Console.WriteLine("canonical_querystring");
Console.WriteLine(canonical_querystring);
var payload_hash = Hash(new byte[0]);//No Payload for GET
var canonical_request = new StringBuilder();
canonical_request.Append("GET\n");
canonical_request.Append(canonical_uri + "\n");
canonical_request.Append(canonical_querystring + "\n");
canonical_request.Append(canonical_headers + "\n");
canonical_request.Append(signed_headers + "\n");
canonical_request.Append(payload_hash);
Console.WriteLine("canonical_request");
Console.WriteLine(canonical_request);
var string_to_sign = $"{algorithm}\n{amzdate}\n{credential_scope}\n" + Hash(Encoding.UTF8.GetBytes(canonical_request.ToString()));
Console.WriteLine("string_to_sign");
Console.WriteLine(string_to_sign);
var signing_key = GetSignatureKey(_secret_key, datestamp, region, service);
var signature = ToHexString(HmacSHA256(signing_key, string_to_sign));
var signed_querystring = canonical_querystring+"&X-Amz-Signature=" + signature;
return signed_querystring;
}
GetSig4QueryString("myApiId.execute-api.us-east-1.amazonaws.com","execute-api","us-east-1");
//Returned String --> X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential= AKIAIOSFODNN7EXAMPLE%2F20190104%2Fus-east-1%2Fexecute-api%2Faws4_request&X-Amz-Date=20190104T190309Z&X-Amz-SignedHeaders=host&X-Amz-Signature=7b830fce28f7800b3879a25850950f6c4247dfdc07775b6952295fa2fff03f7f
Full Endpoint Becomes -
https://myApiId.execute-api.us-east-1.amazonaws.com/dev/myApigNodeJS?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20190104%2Fus-east-1%2Fexecute-api%2Faws4_request&X-Amz-Date=20190104T190309Z&X-Amz-SignedHeaders=host&X-Amz-Signature=7b830fce28f7800b3879a25850950f6c4247dfdc07775b6952295fa2fff03f7f
Note -
This example code refers methods & variables from Github project I gave above.
Also, this example hard coded API Path /dev/myApigNodeJS and signs it and it will be different for you with full absolute path.
AWS recommends to sign all queryStrings, headers which you are planning to send in request. Go through .NET code of library I referred and understand how its doing that.
Let me know if you have questions.
When generating the presigned url a websocket service within the AWS API Gateway, I used the solution by Imran and added the "X-Amz-Security-Token" which is required.
I am new to AWSLex. I was trying to fetch the SlotType information through ModelBuilding GetSlotType API.
The request URL: GET /slottypes/name/versions/version HTTP/1.1
To fetch the latest version as per document is Pattern: \$LATEST|[0-9]+
Through Postman I am able to execute with version number.But while fetching the latest version $LATEST, the URL I used is
https://models.lex.us-east-1.amazonaws.com/slottypes/serviceType/versions/%24LATEST
which worked perfectly through Postman.
The same url when I had used to create the signature didn't work. I get following error
"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."
The canonical request what I had generated is below
GET
/slottypes/serviceType/versions/%24LATEST
content-type:application/json
host:models.lex.us-east-1.amazonaws.com
x-amz-date:20180809T052553Z
content-type;host;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Any help on how to fix the issue is highly appreciated.
String payloadHash = CommonUtil.hexEncode(sha256Hash(payload));
ZonedDateTime utcNow = Instant.now().atZone(ZoneOffset.UTC);// Date
for headers and the credential string
String amzDate =
utcNow.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
String dateStamp =
utcNow.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String canonicalHeaders = String.format("content-type:%s\nhost:%s\nx-
amz-date:%s\n", contentType, host, amzDate);
String canonicalRequest = String.format("%s\n%s\n%s\n%s\n%s\n%s",
method, canonicalUri, canonicalQueryString, canonicalHeaders,
signedHeaders, payloadHash);
String credentialScope = String.format("%s/%s/%s/aws4_request",
dateStamp, region, service);
String canonicalRequestHash =
CommonUtil.hexEncode(sha256Hash(canonicalRequest));
String stringToSign = String.format("%s\n%s\n%s\n%s", algorithm,
amzDate, credentialScope, canonicalRequestHash);
byte[] signatureKey = CommonUtil.getSignatureKey(secretKey,
dateStamp, region, service);
String signature = CommonUtil.hexEncode(CommonUtil.HmacSHA256(URLEncoder.encode(stringToSign,"utf-8"), signatureKey));
String authorizationHeader = String.format("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", algorithm, accessKey, credentialScope, signedHeaders, signature);
MultivaluedMap<String, String> headersMap = new MultivaluedHashMap<>();
headersMap.add("Content-Type", contentType);
headersMap.add("X-Amz-Date", amzDate);
headersMap.add("Authorization", authorizationHeader);
This is how I am generating the signature.The same code works fine with version as number. The issue lies when I encode the url with $LATEST as value and pass it.
i try to register a user in my amazon cognito user pool with username and password from my java backend but i always get the error:
Unable to verify secret hash for client
in the documentation i don't found any information how to pass the clientSecret in the register request and i don't like to create an (backend) app without a clientSecret.
My code looks like this
identityProvider = AWSCognitoIdentityProviderClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(awsCreds)).withRegion(Regions.EU_CENTRAL_1).build();
Map<String, String> authParameters = new HashMap<>();
authParameters.put("USERNAME", "username");
authParameters.put("PASSWORD", "password");
authParameters.put("SECRET_HASH", "secret copy and paste from the aws console"); // i read in a forum post, that this should work
AdminInitiateAuthRequest authRequest = new AdminInitiateAuthRequest();
authRequest.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH);
authRequest.setAuthParameters(authParameters);
authRequest.setClientId("clientId");
authRequest.setUserPoolId("userPoolId");
AdminInitiateAuthResult authResponse = identityProvider.adminInitiateAuth(authRequest);
Thanks
Marcel
To register users you should use the SignUp API. The secret hash can be calculated as follows in Java:
public String calculateSecretHash(String userPoolclientId, String userPoolclientSecret, String userName) {
if (userPoolclientSecret == null) {
return null;
}
SecretKeySpec signingKey = new SecretKeySpec(
userPoolclientSecret.getBytes(StandardCharsets.UTF_8),
HMAC_SHA256_ALGORITHM);
try {
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(signingKey);
mac.update(userName.getBytes(StandardCharsets.UTF_8));
byte[] rawHmac = mac.doFinal(userPoolclientId.getBytes(StandardCharsets.UTF_8));
return Encoding.encodeBase64(rawHmac);
} catch (Exception e) {
throw new RuntimeException("Error while calculating ");
}
}
Can you please elaborate your use case of creating users from your backend instead of directly calling Amazon Cognito from your clients?
Edit: We have updated our documentation to include a section about how to compute the secret hash.
The following code works perfectly:
AdminInitiateAuthRequest adminInitiateAuthRequest = new AdminInitiateAuthRequest().withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH).withClientId("<ID of your client application>").withUserPoolId("<your user pool ID>")
.addAuthParametersEntry("USERNAME", "<your user>").addAuthParametersEntry("PASSWORD", "<your password for the user>");
AdminInitiateAuthResult adminInitiateAuth = identityProvider.adminInitiateAuth(adminInitiateAuthRequest);
System.out.println(adminInitiateAuth.getAuthenticationResult().getIdToken());
I am facing an issue when trying to create a APNS Platform Application on SNS using .NET AWS SDK.
I have .p12 certificate, So I can able to extract the encoded base64 string between ---BEGIN CERTIFICATE---- and ---END CERIFICATE------ for certificate. Then I have no idea how to extract private key(not RSA Private Key) from .p12 certificate.
When creating an ios platform application we need to pass PlatformPrincipal(Certificate) and PlatformCredential(Private Key) attributes.
I can able to extract Certificate from X509Cerificate2.Rawdata Property , But no idea to get private key.
I directly pass cerificate and Private key encoded base64 strings , then it is working find and able to generate PlatformApplicationArn. But this is not the case, I should extract those credentials from .P12.
Please kindly help me regarding this.
public string createAPNSPlatformApplication(string platform, string appName, string P12Password)
{
byte[] appleCert = null;
string p12File = #"cert.p12";
try
{
appleCert = File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "P12\\" + p12File));
}
catch (Exception ex)
{
throw ex;
}
// Load your certificate from file
X509Certificate2 certificate = new X509Certificate2(appleCert, "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
string certData = "-----BEGIN CERTIFICATE-----\n" + Convert.ToBase64String(certificate.RawData) + "\n-----END CERTIFICATE-----";
bool flag = certificate.HasPrivateKey;
string key = Convert.ToBase64String(certificate.Export(X509ContentType.Pkcs12, "Sparity123"));
Dictionary<string, string> dictionaryPlatformApplicationAtt = new Dictionary<string, string>();
//dictionaryPlatformApplicationAtt.Add("PlatformPrincipal", "-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----");
dictionaryPlatformApplicationAtt.Add("PlatformPrincipal", certData);
dictionaryPlatformApplicationAtt.Add("PlatformCredential", "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0cNX+ITZStS1v/6CJ7xBMK8YxpbGCXt4ylodWfE9AfyqfjSq00nzfjFC0ICTbPHJE/O7klZ7oYRtecwqBTlp8av1VUldWN0R1cQealdKCQtCkH03WcfUD+P4VRzncHlSBl+mx2V349/iOICMfz9lwMe1/enWEJsONNRlm4hxXSxF/gBRDYMwTLplodZkR1SnfZcPnR77/1vDY+UAWQFtFShG4Id8hrxHWXlnoB3HmpigBfJdgP9c+unYT+spxb6ZU11qsKr4qT8DBMQn/hxr4Rk2waktqJTx/A1ZHxQoFQH5XOONF+rOTAILN/Ap1/LihRsFPUp8DLMpY0hS+U4qLAgMBAAECggEBAKyJQHXRuCn5yErRueZqy8CqEseiUkwA/qKMwTh3gSiA8HbD+1QP+QuhFgJGwjdIY1OIL74vO1WklREJG8Bs9aUGgNq284AjNLi/ra+pFzMrRGnK7RjiupBj5EDgbHkIH3ITmohO+sZ4BhmQrUVb7+SSOTCuHfSRRGefoHx6NPNZ3xN2AyPUpf2tiZR8DgIHh5huwGKs71pCQwf8G0ID+vmQl5AKWPwo9oLLei1r2fZT1+pC0QppFmNpGYSqkOJTfY5W0bVf/JM5aSEQdS4e9qAHPNEFCt+OgZ0Hmvrzo3aL2jpSia/7u7VE53Hn4vCAoHRYPH36tC5cK30Qi3sXqoECgYEA4VE5FGNj2XxzlCDAzOf49qGZdkwlSCO0VjbC1H5HiisZml5MzWwvPF3dW6jEuXeEM6g5mGbkuj7iqJWw8RLcZIl0zKyctxeNebsBYhyqzz0JICucY1/cb1zM6vR6tw/7uePON62ZZbSgnKKAK2p1L3baOdBOMhqNjNQqg37TcKsCgYEAzQMssE4Xhr1QCJjfGe+ubGrU0kksRtaSoUI9VeBhV/R4BGA4yqqI9yyX+vvGGfQ+c5WWlL0h0eNGBkDbC5re2V7dkx7Ibdve8hFESwD73OGCXEa+SQTpLfQecpH0OUcv1PhxP1H+zea9Dc8QbaoI9z19l/dzZuYvk9L9wwXjDaECgYAnQjq61bKBI5ET3z0YttaHSpLtrMjWL/ptBy+dTL90UJwubH3fURlN4GEAZoCYfD2ZrpcSeeMinHBrxWFGL+4cymKCgxTvRfhNnIraKHqOj2j2pQKjwgErmjTOP3jeSrempQ26X9IZiYZupFXGus7eTUwqxKPLURsWzaT2snNu7wKBgQCU1hgsixVDac9f3nQiJULxTQHjBOKTLxZW4nsbDII/U6mKK70vTW2QXRydRwpEL3DjgxdGNqUF9sUQpycfnrjpAJlGRNPoBfcusGBvP3CapFF64pUk99690PwF23Z92vOb3x6AhOvC2iAolUptA0PtEwRKPojWjIi//i7i1jRH4QKBgDc952mfjZrMXP78Duy6bWqhtgCGK02jwTFk0NgAII2KIlT65Lj3gekxvYRNmeU8TW4know6YQm5OZ2Pyj9paJlkhvrE/t/cnn3hfVQkDbtL/iY7HuigupM2Z50EkWlu45Kzej+AO4J2/wSxlzGnHrOubtAfMrRv+dPSMqm8xKU0\n-----END PRIVATE KEY-----");
var resp = client.CreatePlatformApplication(new CreatePlatformApplicationRequest()
{
Attributes = dictionaryPlatformApplicationAtt,
Name = appName,
Platform = platform
});
return resp.PlatformApplicationArn;
}
Thanks in advance.