Forum Discussion

Ish's avatar
Ish
New Contributor III
7 years ago

AWS Signature for REST Get & Post

Hello,

Is there a way to call an AWS API using the SnapLogic REST Get or Post snaps?

There’s no option to create an AWS Signature account for REST:

Hopefully I’m just missing something. I did all my testing using Postman and now I’m blocked in SnapLogic.

In SnapLogic Expression Language I see there’s Digest sha256 but AWS requires HMAC so I can’t find a workaround.

Alternatively is HMAC available in the Script snap or do I need to develop my own Snap for this this?

Thanks!

9 Replies

    • Kire's avatar
      Kire
      New Contributor

      I tried making Rest AWS v4 Sig account, but it will not work, while the script from earlier worked perfectly (after few modifications in the timestamp, it wouldn’t work as it is on every time zone it seems). Now i can’t make the account work itself. I get the 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.” From my experience with the script earlier, could it be a bug in the signature generation code that it would not work properly for different time zones? I am 100% positive I used right credentials, since I copied them from same source that I have used for the script. I would not mind continue using the script itself, but the problem is the credentials remain hardcoded in the script code and are visible. Using an account would solve that problem.

    • Ish's avatar
      Ish
      New Contributor III

      Hi @Siva_Venna,

      The advice I can offer is to check the values of the variables you input to the script are correct. For example if the endpoint is something like:

      https://subdomain.domain.com/v1/resource?token=abcd123

      Then the values would be:

          var endpoint = 'https://subdomain.domain.com/v1/resource?token=' + encodeURIComponent('abcd123');
          var host = 'subdomain.domain.com';
          var canonical_uri = '/v1/resource';
          var canonical_querystring = 'token=' + encodeURIComponent('abcd123');
      
  • Ish's avatar
    Ish
    New Contributor III

    I was able to implement this using a Script Snap. Code for the Script Snap is below in case this can help someone else. Please note this is only for a GET request, and also note the HTTP Headers in the REST Get Snap.

    /*
    Purpose of this script is to derive a signing key and signature for AWS Signature Version 4 to enable calling an AWS based API GET method
    https://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
    https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
    */
    
    try { load("nashorn:mozilla_compat.js"); } catch(e) { }
    
    importPackage(com.snaplogic.scripting.language);
    importPackage(java.util);
    
    importClass(javax.crypto.Mac);
    importClass(javax.crypto.spec.SecretKeySpec);
    importClass(org.apache.commons.codec.binary.Base64);
    importClass(org.apache.commons.codec.digest.DigestUtils);
    importClass(javax.xml.bind.DatatypeConverter);
    
    var impl = {
        input : input
        , output : output
        , error : error
        , log : log
    
        ,execute : function () 
        {
            //----------------------------------------------------------
            // Enter your variables here
            //----------------------------------------------------------
            var secretKey = ''; // Your Secret Key
            var accessKey = ''; // Your Access Key
            var regionName = ''; //e.g.: us-east-1
            var serviceName = ''; //e.g.: execute-api
            var endpoint = ''; //e.g.: https://subdomain.domain.com/resource/id
            var host = ''; //e.g.: subdomain.domain.com
            var canonical_uri = ''; //e.g.: /resource/id
            var canonical_querystring = ''; //e.g.: Param1=value&Param2=value
            //----------------------------------------------------------
            
            var method = 'GET';
            var dateStamp = impl.getDateStamp();
            var amzdate = impl.getDateTimeStamp();
            var canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n';
            var signed_headers = 'host;x-amz-date';
            var payload_hash =  DigestUtils.sha256Hex('');
            var canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash;
            var algorithm = 'AWS4-HMAC-SHA256';
            var credential_scope = dateStamp + '/' + regionName + '/' + serviceName + '/' + 'aws4_request';
            var string_to_sign = algorithm + '\n' +  amzdate + '\n' +  credential_scope + '\n' +  DigestUtils.sha256Hex(canonical_request); 
            var signing_key = impl.getSignature(secretKey, dateStamp, regionName, serviceName);
            var signature = impl.base64toHEX(impl.HmacSHA256( string_to_sign, signing_key ));
            var authorization_header = algorithm + ' ' + 'Credential=' + accessKey + '/' + credential_scope + ', ' +  'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature;
            
            var wrapper = new java.util.HashMap();
            wrapper.put("authorization", authorization_header );
            wrapper.put("url", endpoint );
            wrapper.put("host", host );
            wrapper.put("amzdate", amzdate );
            
            this.output.write(wrapper);
        }
        
        , getSignature : function ( secretKey, dateStamp, regionName, serviceName ) 
        {
            regionName = regionName || '';
            serviceName = serviceName || '';
            dateStamp = dateStamp || '';
            
            var kSecret = (new java.lang.String( "AWS4" + secretKey )).getBytes("UTF8");
            var kDate = impl.HmacSHA256(dateStamp, kSecret);
            var kRegion = impl.HmacSHA256(regionName, kDate);
            var kService = impl.HmacSHA256(serviceName, kRegion);
            var kSigning = impl.HmacSHA256("aws4_request", kService);
            return kSigning;
        }
        
        , HmacSHA256 : function ( data, secretKey ) 
        {
            var algorithm = "HmacSHA256";
            var mac = Mac.getInstance( algorithm );
            mac.init(new SecretKeySpec( secretKey, algorithm ) );
            return mac.doFinal( data.getBytes("UTF8") );
        }
        
        , getDateStamp : function ()
        {
            // Must be YYYYMMDD format
            var d = new Date();
            var dateStamp = d.getFullYear();
            dateStamp += ('0' + (d.getMonth()+1)).slice(-2);
            dateStamp += ('0' + d.getDate()).slice(-2);
            return dateStamp;
        }
    
        , getDateTimeStamp : function ()
        {
            // Must be YYYYMMDDTHHMMSSZ format
            var d = new Date();
            var dateTimeStamp = impl.getDateStamp();
            dateTimeStamp += 'T';
            dateTimeStamp += ('0' + d.getHours()).slice(-2);
            dateTimeStamp += ('0' + d.getMinutes()).slice(-2);
            dateTimeStamp += ('0' + d.getSeconds()).slice(-2);
            dateTimeStamp += 'Z';
            return dateTimeStamp;
        }
        
        , base64toHEX : function( guid ) 
        {
            return DatatypeConverter.printHexBinary( guid ).toLowerCase();
        }
        
    };
    
    var hook = new com.snaplogic.scripting.language.ScriptHook(impl);
    
    • Harikrish's avatar
      Harikrish
      New Contributor

      Hi Ish,

      Can you confirm from which Snaplex are you trying to connect?
      Also can you please mention the snap pack version.
      We too are facing the same issue and despite using the above mentioned script, we receive Signature Not matched error.

      Regards
      Harikrishnan