How to send Outbound REST Messages using AWS Signature Version 4?

spinol
Kilo Explorer

Currently trying to send outbound REST messages using AWS sigv4 authentication. I have everything working in POSTMan but when I try to implement the same headers within ServiceNow, I end up with 502 errors. Can anyone assist?

Thanks!

13 REPLIES 13

As of now is there any method to implement  Outbound REST Messages using AWS Signature Version 4?

 

walter_brame
ServiceNow Employee
ServiceNow Employee

Here is a script I have used to download a file from a non-public bucket in Amazon AWS S3. Pay close attention to the "REPLACE_ME" comments to make sure you put the correct value there and take a look at the "GeneralAWSAuthorization.test" static method for execution example.

 

/*
* Constructs the Authentication Header for Amazon AWS S3.
*
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#ConstructingTheAuthenticationHeader
* @author SERVICE-NOW.COM\walter.brame
*/
var GeneralAWSAuthorization = Class.create(  );
GeneralAWSAuthorization.prototype = {
    initialize: function (  ) {	
		
		this.stringToSign            = null;		
		this.canonicalizedAmzHeaders = null;		
		this.contentMD5              = null;
		this.contentType             = null;
		this.date                    = null;	
		this.digest                  = null;
		this.yourSecretAccessKeyID   = ''; // REPLACE_ME
		this.awsAccessKeyId          = ''; // REPLACE_ME
		this.canonicalizedResource   = ''; // REPLACE_ME, Relative URI E.g. /walter.brame/a+test.xlsx
		this.httpVerb                = 'GET'; // REPLACE_ME, E.g. GET, POST, PUT, etc.
    },
	
	getCanonicalizedAmzHeaders : function (  ) {
		return this.canonicalizedAmzHeaders;
	},
	
	setCanonicalizedAmzHeaders : function (canonicalizedAmzHeaders) {
		this.canonicalizedAmzHeaders = canonicalizedAmzHeaders;
	},
	
	getCanonicalizedResource : function (  ) {
		return this.canonicalizedResource;
	},
	
	setCanonicalizedResource : function (canonicalizedResource) {
		this.canonicalizedResource = canonicalizedResource;
	},
	
	getHTTPVerb : function () {
		if ( gs.nil(this.httpVerb) ) {
			this.httpVerb = 'GET';
		}
		return this.httpVerb;
	},
	
	setHTTPVerb : function (httpVerb) {
		this.httpVerb = httpVerb;
	},
	
	getContentMD5 : function () {
		return this.contentMD5;
	},
	
	setContentMD5 : function (contentMD5) {
		this.contentMD5 = contentMD5;
	},
	
	getContentType : function () {
		return this.contentType;
	},
	
	setContentType : function (contentType) {
		this.contentType = contentType;
	},
	
	getYourSecretAccessKeyID : function (  ) {
		return this.yourSecretAccessKeyID;
	},
	
	setYourSecretAccessKeyID : function (yourSecretAccessKeyID) {
		this.yourSecretAccessKeyID = yourSecretAccessKeyID;
	},
	
	getAWSAccessKeyId : function (  ) {
		return this.awsAccessKeyId;
	},
	
	setAWSAccessKeyId : function (awsAccessKeyId) {
		this.awsAccessKeyId = awsAccessKeyId;
	},
	
	getDate : function (  ) {
		if ( gs.nil(this.date) ) {
			var gdt = new GlideDateTime(  );
			var time = gdt.getTime(  );
			var t = time.getByFormat("HH:mm:ss");			
			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 = gdt.getDayOfMonthUTC(  );
			var m = (function (  ) {
				var map = {
					'1'  : 'Jan',
					'2'  : 'Feb',
					'3'  : 'Mar',
					'4'  : 'Apr',
					'5'  : 'May',
					'6'  : 'Jun',
					'7'  : 'Jul',
					'8'  : 'Aug',
					'9'  : 'Sep',
					'10' : 'Oct',
					'11' : 'Nov',
					'12' : 'Dec'
				};
				return map[gdt.getMonthUTC(  ) + ''];
			})();	
			var y = gdt.getYearUTC(  );
			
			this.date = wd + ', ' + d + ' ' + m + ' ' + y + ' ' + t + ' +0000';
			
			gs.print('date = ' + this.date);
		}
		return this.date;		
	},
	
	setDate : function (date) {
		this.date = date;
	},
	
	_getStringToSign : function (  ) {
		if ( gs.nil(this.stringToSign) ) {
			
			if ( gs.nil(this.getHTTPVerb(  )) ) {
				throw 'HTTP Verb is required';
			}
			
			if ( gs.nil(this.getCanonicalizedResource(  )) ) {
				throw 'CanonicalizedResource is required';
			}
			
			this.stringToSign = this.getHTTPVerb(  ) + '\n';
			
			if ( !gs.nil(this.getContentMD5(  )) ) {
				this.stringToSign += this.getContentMD5(  ) + '\n';
			} else {
				this.stringToSign += '\n';
			}
			
			if ( !gs.nil(this.getContentType(  )) ) {
				this.stringToSign += this.getContentType(  ) + '\n';
			} else {
				this.stringToSign += '\n';
			}
			
			this.stringToSign += this.getDate(  ) + '\n';
			
			if ( !gs.nil(this.getCanonicalizedAmzHeaders(  )) ) {
				this.stringToSign += this.getCanonicalizedAmzHeaders(  );
			}	
			
			this.stringToSign += this.getCanonicalizedResource(  );
			
			gs.print('stringToSign = ' + this.stringToSign);
		}
		return this.stringToSign;
	},
	
	_getDigest : function(  ) {		
		if ( gs.nil(this.digest) ) {
			
			if ( gs.nil(this.getYourSecretAccessKeyID(  )) ) {
				throw 'YourSecretAccessKeyID is required';
			}			
			
			try {
				
				var data = this._getStringToSign(  );
				var fkey = this.getYourSecretAccessKeyID(  );
				// default to something JDK 1.4 has
				var MAC_ALG = "HmacSHA1";
				
				this.digest = SncAuthentication.encode(data, fkey, MAC_ALG);

			} catch (e) {
				gs.print(e.toString(  ));
				throw 'failed_missing_requirement';
			}  
		}
		return this.digest;
	},
	
	getAuthorization : function (  ) {
		if ( gs.nil(this.authorization) ) {
			
			if ( gs.nil(this.getAWSAccessKeyId(  )) ) {
				throw 'AWSAccessKeyId is required';
			}
			
			this.authorization = "AWS" + " " + this.getAWSAccessKeyId(  ) + ":" + this._getDigest(  );
		
			gs.print('authorization = ' + this.authorization);
		}
		return this.authorization;
	},
	
    type: 'GeneralAWSAuthorization'
};

GeneralAWSAuthorization.test = function (  ) {	
	try{
		
		// step 1: get the file		
		var request  = new sn_ws.RESTMessageV2(  );  		
		request.setHttpMethod('get');      
		request.setEndpoint('https://s3-us-west-1.amazonaws.com/.../a+test.xlsx');	// REPLACE_ME
		var auth = new GeneralAWSAuthorization(  );		
		request.setRequestHeader('Authorization', auth.getAuthorization(  ));	
		request.setRequestHeader('Date', auth.getDate(  ));	
		
		// step 2 : save the file to a data source.
		// The "data source" record should be manually created prior
		// to doing this		
		var tablename = 'sys_data_source';
		var recordSysId = 'sys_data_source_sys_id'; // REPLACE_ME
		var filename = 'file_name'; // REPLACE_ME
		request.saveResponseBodyAsAttachment(tablename, recordSysId, filename);
		
		var response = request.execute(  );  
		var httpResponseStatus = response.getStatusCode(  ) + '';
      
		gs.print(" http response status_code:  " + httpResponseStatus);
				
		if ( httpResponseStatus != '200' ) {
			gs.print(" http response :  " + response.getBody(  ));
		}	
		
		// step 3 : Run the import and transform for the data source.
		// the "import set table" and "transform map",
		// and "scheduled data import" records
		// should be manually created prior to doing this		
		var scheduledDataLoad = new GlideRecord('scheduled_import_set');
		scheduledDataLoad.get('scheduled_import_set_sys_id'); // REPLACE_ME
		
		SncTriggerSynchronizer.executeNow(scheduledDataLoad);
  }
  catch(ex){      
    gs.print(ex);    
  }
};

 

 

This is a long shot, and considering this post was 3 years ago, I hope you are still around and can assist me.

I have followed your instructions from the code and have replaced the various objects with what is applicable to me, but when I execute the GeneralAWSAuthorization function, I get an error stating the following:

Request not sent to uri= https://<host>/<bucket>/<file> : java.net.ConnectException: Connection refused (Connection refused)
*** Script: http response status_code: 0
*** Script: http response :

 

I've checked the credentials to make sure they are ok and I can access the host URL.

I would really appreciate your assistance in solving this.

Thank you in advance

boz
Tera Expert

Hi,

Bit late to the party, but i'm also looking to authenticate rest messages with AWS Signature Version 4. I came across this SNow doc article that looks promising: https://docs.servicenow.com/en-US/bundle/utah-platform-security/page/product/credentials/task/config...

 

Haven't tried it yet, but i'm guessing its one native way to authenticate with AWS_IAM and AWS Signature Version 4