Issue with generating "AWS Signature Version 4" for REST requests in OOB ServiceNow.

Nikolay Mikheev
Tera Contributor

Hi, community, I need your help. I've spent a few days trying to get ServiceNow to generate the correct Signature for Amazon services (AWS), but seems like it doesn`t work.

Here's what I'm trying to do:

find_real_file.png

  • I`ve created the AWS credentials as described in the same article, using the algorithm created earlier and data for test Amazon account:
    access key: AKIDEXAMPLE
    secret: wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY

    find_real_file.png

 

  • Then I try to create a signature according to the test parameters that are specified in the Amazon programming guide and the signature that is generated by my script in ServiceNow doesn`t match the signature that appears in the Amazon examples.

Checking the notes from manual:

Note: Amazon V4 signature based authentication can also be used from Script background.

So use it in is my script:
 

var service = 'iam';
var region = 'us-east-1';
var credentialSID = '<SID of the my Credential record>'; 
var content = 'Action=ListUsers&Version=2010-05-08';
var gdt = new GlideDateTime("2015-08-30 12:36:00");

		// Define the HttpRequestData object
		var host = service + "." + region + ".amazonaws.com";
		var endpoint = "https://" + host ;
		var httpRequestData = new sn_auth.HttpRequestData();
		var dateNum = gdt.getNumericValue();

		httpRequestData.setEndpoint(endpoint);
		httpRequestData.setHost(host);
		httpRequestData.setRegion(region);
		httpRequestData.setService(service);
		httpRequestData.setHttpMethod('get');
		httpRequestData.setDate(dateNum);
		httpRequestData.setContent(content);
		
		var credential = (new sn_cc.StandardCredentialsProvider()).getAuthCredentialByID(credentialSID);
		// Create the RequestAuthAPI object and sign the request
		var signingAPI = new sn_auth.RequestAuthAPI(httpRequestData, credential);
		var signMessage = signingAPI.generateAuth();		
		gs.log("Status is: " + signMessage.getStatus());
		
		//------------------------------------------------------------------------
		var headerMap = signMessage.getHeaderMap();

		for(var x in headerMap) {
			var y = headerMap[x];
			gs.log('HeaderMap[' + x + ']: ' + y);
		}
		
		var tmpDate = '20150830T123600Z';
		var tmpAuth = 'AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7'
		gs.log('ShouldBe[Authorization]: ' + tmpAuth);
		gs.log('ShouldBe[X-Amz-Date]: ' + tmpDate);
		
Status is: SUCCESS
HeaderMap[Authorization]: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=host;x-amz-date, Signature=bdb91ff1bb6546c3ea5bff7fc1463037b0a0879ab7e0942402ec90d0d128fda6
HeaderMap[X-Amz-Date]: 20150830T123600Z
ShouldBe[Authorization]: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7
ShouldBe[X-Amz-Date]: 20150830T123600Z


And, of course any try to send the request with generated that way signature failed with response status 403:

<ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <Error>
    <Type>Sender</Type>
    <Code>SignatureDoesNotMatch</Code>
    <Message>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.</Message>
  </Error>
  <RequestId>f9ba7ffa-70a0-505e-9a16-250f2b0ab95b</RequestId>
</ErrorResponse>


The error is only in the signature Request, because if I manually substitute a signature from Postman in the same request in Snow (hardcoded in the script, yes) , Amazon accepts the requests.

The same incorrect result is reproduced with my personal credential for AWS, on different SNow instances and different versions of ServiceNow (Rome)  which I compare with the Signature results obtained via Postman for the same requests.

Any thoughts?
Regards, Nikolay.

11 REPLIES 11

Anusha Reddy VK
Mega Guru

Hey All,

I have been facing the same issue while generating the signature. But after few tweaks to the code in my BR, it succesfully created signing request and sent Incident information to AWS SQS Queue. Below is my code:

(function executeRule(current, previous /*null when async*/ ) {

var method = 'POST';
var service = 'sqs';
var region = 'us-east-1';
var credentialSID = 'AWS Credential SysId';
var num = current.number;
var openedAt = current.sys_created_on;
var caller = current.caller_id;
var channel = current.contact_type;
var priority = current.priority;
var ag = current.assignment_group;
var assignedTo = current.assigned_to;
var sd = current.short_description;
var closeCode = current.close_code;
var closeNotes = current.close_notes;

 

var messageBody = "{\"Incident Number\":\"" + num +
"\",\"Opened at\":\"" + openedAt +
"\",\"Caller\":\"" + caller +
"\",\"Channel\":\"" + channel +
"\",\"Priority\":\"" + priority +
"\",\"Assignment Group\":\"" + ag +
"\",\"Assigned to\":\"" + assignedTo +
"\",\"Short Description\":\"" + GlideStringUtil.base64Encode(sd) +
"\",\"Resolution Code\":\"" + closeCode +
"\",\"Resolution Notes\":\"" + GlideStringUtil.base64Encode(closeNotes) +
"\"}";

 

var endpoint = "Your EndPoint URL Here";

var httpRequestData = new sn_auth.HttpRequestData();

httpRequestData.setEndpoint(endpoint);
httpRequestData.setRegion(region);
httpRequestData.setService(service);
httpRequestData.setHttpMethod(method);
// Calculate the SHA-256 hash of the message body
var digest = new GlideDigest();
var hash = digest.getSHA256Base64(messageBody);
httpRequestData.addHeader('x-amz-content-sha256', hash); //IMPORTANT!!!

httpRequestData.addQueryParam('Action', 'SendMessage');
httpRequestData.addQueryParam('MessageBody', encodeURIComponent(messageBody));
var credential = (new sn_cc.StandardCredentialsProvider()).getAuthCredentialByID(credentialSID);
// Create the RequestAuthAPI object and sign the request
var signingAPI = new sn_auth.RequestAuthAPI(httpRequestData, credential);
var signMessage = signingAPI.generateAuth();
signMessage.setDirective("header");

var restMessage = new sn_ws.RESTMessageV2();
restMessage.setHttpMethod(method);
restMessage.setLogLevel('all');
restMessage.setEndpoint(endpoint);

var queryMap = httpRequestData.getQueryParamMap(); // IMPORTANT
for (var x in queryMap) {
var y = queryMap[x];
gs.log('queryMap[' + x + ']: ' + y);
restMessage.setQueryParameter(x, y);
}

var headerMap = signMessage.getHeaderMap(); // Headers from signed request.
for (var i in headerMap) {
var j = headerMap[i];
gs.log('HeaderMap[' + i + ']: ' + j);
restMessage.setRequestHeader(i, j);
}

var response = restMessage.execute();
gs.info("status = " + response.getStatusCode());
gs.info("body = " + response.getBody());
})(current, previous);

Hope this is helpful.

Thanks,
Anusha

Anusha Reddy VK
Mega Guru

Hey All,

I have been facing the same issue while generating the signature. But after few tweaks to the code in my BR, it succesfully created signing request and sent Incident information to AWS SQS Queue. Below is my code:

(function executeRule(current, previous /*null when async*/ ) { 

var method = 'POST';
var service = 'sqs';
var region = 'us-east-1';
var credentialSID = 'AWS Credential SysId';
var num = current.number;
var openedAt = current.sys_created_on;
var caller = current.caller_id;
var channel = current.contact_type;
var priority = current.priority;
var ag = current.assignment_group;
var assignedTo = current.assigned_to;
var sd = current.short_description;
var closeCode = current.close_code;
var closeNotes = current.close_notes;

 

var messageBody = "{\"Incident Number\":\"" + num +
"\",\"Opened at\":\"" + openedAt +
"\",\"Caller\":\"" + caller +
"\",\"Channel\":\"" + channel +
"\",\"Priority\":\"" + priority +
"\",\"Assignment Group\":\"" + ag +
"\",\"Assigned to\":\"" + assignedTo +
"\",\"Short Description\":\"" + GlideStringUtil.base64Encode(sd) +
"\",\"Resolution Code\":\"" + closeCode +
"\",\"Resolution Notes\":\"" + GlideStringUtil.base64Encode(closeNotes) +
"\"}";

 

var endpoint = "Your EndPoint URL Here";

var httpRequestData = new sn_auth.HttpRequestData();

httpRequestData.setEndpoint(endpoint);
httpRequestData.setRegion(region);
httpRequestData.setService(service);
httpRequestData.setHttpMethod(method);
// Calculate the SHA-256 hash of the message body
var digest = new GlideDigest();
var hash = digest.getSHA256Base64(messageBody);
httpRequestData.addHeader('x-amz-content-sha256', hash); //IMPORTANT!!!

httpRequestData.addQueryParam('Action', 'SendMessage');
httpRequestData.addQueryParam('MessageBody', encodeURIComponent(messageBody));
var credential = (new sn_cc.StandardCredentialsProvider()).getAuthCredentialByID(credentialSID);
// Create the RequestAuthAPI object and sign the request
var signingAPI = new sn_auth.RequestAuthAPI(httpRequestData, credential);
var signMessage = signingAPI.generateAuth();
signMessage.setDirective("header");

var restMessage = new sn_ws.RESTMessageV2();
restMessage.setHttpMethod(method);
restMessage.setLogLevel('all');
restMessage.setEndpoint(endpoint);

var queryMap = httpRequestData.getQueryParamMap(); // IMPORTANT
for (var x in queryMap) {
var y = queryMap[x];
gs.log('queryMap[' + x + ']: ' + y);
restMessage.setQueryParameter(x, y);
}

var headerMap = signMessage.getHeaderMap(); // Headers from signed request.
for (var i in headerMap) {
var j = headerMap[i];
gs.log('HeaderMap[' + i + ']: ' + j);
restMessage.setRequestHeader(i, j);
}

var response = restMessage.execute();
gs.info("status = " + response.getStatusCode());
gs.info("body = " + response.getBody());
})(current, previous);

Hope this is helpful.

Thanks,
Anusha