Outbound REST authentication support for format "AWS Signature"

mayankab
Kilo Explorer

I am trying to make a REST call to one of the endpoint which requires Outbound Rest Authentication format as "AWS Signature". Does ServiceNow offer this kind of authentication using Access Key and Secret Key? Please suggest.

3 REPLIES 3

Pradeep Sharma
ServiceNow Employee
ServiceNow Employee

Miguel Donayre
ServiceNow Employee
ServiceNow Employee

@mayankab 

OOTB ServiceNow can not create an AWS SignatureV4. There are different options to create that signature. The biggest hurdle to create a signature is the Hex Hmac SHA256 conversation. To get your key components to convert, you have to bring in a CryptoJS function. 

The solution that I used was to create a Custom scoped application and drop a CryptoJS function library in a script include and make it accessible from "all application scopes." I called this app "Key Master," and the Script Include- CryptoJS

find_real_file.png

Next you will need to build the AWS SigV4 signature header block. I took the structure that AWS provides and broke it down in a script. I used this script to pull an excel file and attached it to a data source record using REST API. You will have to modify for your needs. 

****Keep in mind you will have to create a scoped application to house one file. This will count against you count for table usage. **** THIS WILL NOT WORK IF YOU DROP IT IN A GLOBAL SCRIPT INCLUDE****

 

// * Constructs the Authentication Header for Amazon AWS S3 SigV4.
//https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
//@auther SERVICE-NOW.COM\MIGUEL.DONAYRE


// SETTING VARABILES
var accessID = '<CHANGE ME>';// This come from AWS
var yourSecretAccessKeyID = '<CHANGE ME>'; // This comes from AWS
var CanonicalResource = '/<CHANGE ME>'; // The file name that you want to retrive 
var region = 'us-east-1'; //region of bucket
var service = 's3'; // service type
var bucket = '<CHANGE ME>'; //bucket name



var uri = '<CHANGE ME>'; 
var CanonicalURI =  encodeURIComponent(uri);
var CanonicalQueryString = '';
var CanonicalHeaders = '';
var SignedHeaders = '';

var digest = new GlideDigest();
var HashedPayload = digest.getSHA256Hex(''); //ServiceNow's method for SHA256 hex
var hash = HashedPayload.toLowerCase();
var date = getDate();
//var jshash = x_snc_key_master.CryptoJS.SHA256(''); // using the crypto js way to SHA256 hex
//var hash = jshash.toLowerCase(); // uncomment out if you use the crytpo js method


//Headers
var header1 = 'host';
var headValue1 = 's3.amazonaws.com';
var header2 = 'x-amz-content-sha256';
var headValue2 = hash;

// Date converter for AWS date format
function getDate(){
	var date;
      {
     var gdt = new GlideDateTime(  );
     var time = gdt.getTime(  );
     var t = time.getByFormat("HHmmss");                                  
     var wd = (function(  ){
     var map = {
        '1' : 'Mon',
    	'2' : 'Tue',
        '3' : 'Wed',
        '4' : 'Thu',
    	'5' : 'Fri',
        '6' : 'Sat',
        '7' : 'Sun'
    };
      return map[gdt.getDayOfWeekUTC(  ) + ''];
	})();
    var d = (function (  ) {
	var map = {
		'1'   : '01',
		'2'   : '02',
        '3'   : '03',
    	'4'   : '04',
		'5'   : '05',
        '6'   : '06',
        '7'   : '07',
        '8'   : '08',
        '9'   : '09',
        '10'  : '10',
        '11'  : '11',
        '12'  : '12',
	    '13'  : '13',
        '14'  : '14',
        '15'  : '15',
        '16'  : '16',
        '17'  : '17',
        '18'  : '18',
        '19'  : '19',
        '20'  : '20',
        '21'  : '21',
        '22'  : '22',
        '23'  : '23',
	    '24'  : '24',
        '25'  : '25',
        '26'  : '26',
        '27'  : '27',
        '28'  : '28',
        '29'  : '29',
        '30'  : '30',
        '31'  : '31'
	};
return map[gdt.getDayOfMonthUTC(  ) + ''];
	})();
												
  	var m = (function (  ) {
    var map = {
       '1'  : '01',
       '2'  : '02',
       '3'  : '03',
       '4'  : '04',
       '5'  : '05',
       '6'  : '06',
       '7'  : '07',
       '8'  : '08',
       '9'  : '09',
       '10' : '10',
       '11' : '11',
       '12' : '12'
     };
 return map[gdt.getMonthUTC(  ) + ''];
    })();         
 var y = gdt.getYearUTC(  );
 date = y + m + d + 'T' + t + 'Z';
	}
return date;                               
    }
 // gs.print('date = ' + date);
  



//Task 1: Create a Canonical Request
//<HTTPMethod>\n
//<CanonicalURI>\n
//<CanonicalQueryString>\n
//<CanonicalHeaders>\n
//<SignedHeaders>\n
//<HashedPayload>


// Creating the Canonical Header
CanonicalHeaders = header1.toLowerCase() + ':' + headValue1.trim() + '\n' +
header2.toLowerCase() + ':' + headValue2 ; 

gs.print ('CanonicalHeaders:' + CanonicalHeaders);

//Signed Headers
SignedHeaders = header1.toLowerCase() + ';' + header2.toLowerCase();

//Create CanonicalRequest
var CanonicalRequest = 'GET' + '\n' +
uri + '\n' +
CanonicalQueryString + '\n' +
CanonicalHeaders + '\n' + '\n' +
SignedHeaders + '\n' +
hash;

gs.print('CanonicalRequest: '+ CanonicalRequest);

//Task 2: Create a String to Sign
//"AWS4-HMAC-SHA256" + "\n" +
//timeStampISO8601Format + "\n" +
//<Scope> + "\n" +
//Hex(SHA256Hash(<CanonicalRequest>))

// Setting Variables needed for String to Sign
var date_sub = date.substring(0,8);
var scope = date_sub + '/' + region + '/' + service + '/' + 'aws4_request';
var hashCanonical = digest.getSHA256Hex(CanonicalRequest);
var hashLower = hashCanonical.toLowerCase();
//var jshashCanonical = x_snc_key_master.CryptoJS.SHA256(CanonicalRequest);// using the crypto js way to hex


var stringToSign = 'AWS4-HMAC-SHA256' + '\n' + 
date + '\n' + 
scope + '\n' + 
hashLower;

//gs.print('StringToSign: ' + stringToSign);



//Task 3: Calculate Signature

//DateKey              = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
//DateRegionKey        = HMAC-SHA256(<DateKey>, "<aws-region>")
//DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
//SigningKey           = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
//Signature 		   = HMAC-SHA256(SigningKey, StringToSign)


var signature = getSig(yourSecretAccessKeyID, date_sub, region, service);
//gs.print('Signature: ' + signature);
  
// This Function creates the encoding of the Signature	
		
function getSig(yourSecretAccessKeyID, date_sub, region, service){
	try{
var kSecret = yourSecretAccessKeyID; 
var kDate = x_snc_key_master.CryptoJS.HmacSHA256(date_sub, 'AWS4' + kSecret); 
var kRegion = x_snc_key_master.CryptoJS.HmacSHA256(region, kDate); 
var kService = x_snc_key_master.CryptoJS.HmacSHA256(service, kRegion);
var kSigning = x_snc_key_master.CryptoJS.HmacSHA256('aws4_request', kService);
var kSignature = x_snc_key_master.CryptoJS.HmacSHA256(stringToSign,kSigning);
		
		//gs.print('kSignature: ' + kSignature);
		
return kSignature;
		
	}
	catch(e){
		gs.log(e.string());
		throw 'failed_missing_requirement';
	}
}


//Authorization header
getS3(authorization, date, headValue2);


//Create Authorization String

var authorization = 'AWS4-HMAC-SHA256 Credential=' + accessID + '/' + scope + ', SignedHeaders=' + SignedHeaders + ', Signature=' + signature;
//gs.print('authorization: ' + authorization);

// Creating the GET REST API 
function getS3(auth, date, headValue2){
	try{
		var r = new sn_ws.RESTMessageV2();
		r.setHttpMethod('get');
		r.setEndpoint('<CHANGE ME>'); // TAGET S3 BUCKET URL
		r.setRequestHeader('Authorization', auth);
		r.setRequestHeader('Date', date);
		r.setRequestHeader('X-Amz-Content-Sha256', headValue2);
		r.saveResponseBodyAsAttachment('sys_data_source','<SYS_ID TO DATA SOURCE>','<NAME FILE YOU WANT ATTACHED>');
		var response = r.execute();
		
	
		
	}
	
	catch(ex){
		var message = ex.message;
		gs.print(message);
	}


		//gs.print('This is the Body: ' + response.getBody()); // IF you do not get a code 200 , this might tell you whats wrong
		//gs.print('Status: ' + response.getStatusCode()); // code 200 is what you want

}



 

 if you go to the AWS example, you can plug in the example values they provide so you can see it give you the correct values. https://docs.aws.amazon.com/general/latest/gr/signature-v4-test-suite.html

 

I hope that helps out!

 

Hey @Miguel Donayre 

I'm trying to use what you created in flow designer. I'm not having any luck. Do you know if it's possible? I created a script step in flow designer and put the code for constructing the signature there and created a custom scoped application for the cryptojs file.