Home SalesforceApex AWS Signature 4 Signing in Salesforce

AWS Signature 4 Signing in Salesforce

by Dhanik Lal Sahni

We use different AWS services in Salesforce for different use cases like storage, cloud computing, database services, queue service, etc. To integrate Salesforce and AWS, we have to use the AWS Signature Signing process. Salesforce provides a standard process to authenticate using Named Credential but sometimes we need to integrate custom API hosted on the AWS cloud. To use such a custom API, we need to use a custom AWS Signature 4 signing process. This post will explain how to generate an authentication token using AWS Signature 4 Signing in Salesforce Apex.

Let us see what is AWS Signature 4 and its benefit.

Signature Version 4 (SigV4) is the process to add authentication information to AWS API requests sent by HTTP. Authentication with AWS Signature Version provides the following benefits

  • Verification of the identity of the requester
  • In-transit data protection
  • Protect against reuse of the signed portions of the request 

Let us see how we can generate an authentication token using AWS Signature Version 4 in Apex. As per AWS deocumentation below steps are required to generate the token

  1. Create a canonical request
  2. Create a string to sign
  3. Calculate the signature
  4. Add the signature to the HTTP request

1. Create a canonical request

The signing process first creates a string that includes information from our request in a standardized (canonical) format. This ensures that when AWS receives the request, it can calculate the same signature that we calculated. It is a kind of checksum process to verify what we receive is what we sent.

private string createCanonicalRequest()
{
        //Task1. Step 2 - Add the canonical URI parameter
        String canonicalUri = '/'+resource; 
        
         //Task1. Step 3 - Add the canonical query string,
        String canonicalQueryString = '';
        
        //Task1. Step 4 - Add the canonical headers
        string canonicalHeaders=canonicalHeaders();
        
        //Task1. Step 5 - Add the signed headers
        String signedHeaders =signedHeaders();
        
        //Task1. Step 6 - Use a hash (digest) function like SHA256 to create a hashed value from the payload in the body of the HTTP or HTTPS request. 
        Blob payload = Blob.valueOf('');
        String payloadHash = EncodingUtil.convertToHex(Crypto.generateDigest('SHA-256', payload));
        
        //Task1. Step 7: Combine elements to create create canonical request  
        String canonicalRequest = method + '\n' 
            + canonicalUri + '\n'  
            + canonicalQueryString + '\n' 
            + canonicalHeaders + '\n' 
            + signedHeaders + '\n' 
            + payloadHash;
        return canonicalRequest;
}

2. Create a string to sign

The string to sign includes meta-information about our API request and about the canonical request that we have created in the first step. Date format is very important in this step.

private string createStringToSign(string canonicalRequest)
{
        //Task2 - Step 1 algorithm designation
		String algorithm = algorithm();
        
        //Task2 - Step 2 Append the request date value
        String credentialScope = credentialScope();
        
        //Task2 - Step 3 Append the credential scope value, 
        String stringToSign = algorithm + '\n' +  amzdate + '\n' +  credentialScope + '\n' + 
            EncodingUtil.convertToHex(Crypto.generateDigest('sha256', Blob.valueOf(canonicalRequest)));
       
	return stringToSign;
}

3. Calculate the signature

This step will calculate the signature for the request string based on AWS secret, region, and service.

private string calculateSignature(string stringToSign)
{
    //Task3 - Step 1 generate signing key
    Blob signingKey = createSigningKey(secret);
    
    //Task3 - Step 2 generate signature  
    String signature =  createSignature(stringToSign, signingKey); 
    
    return signature;
}

4. Add the signature to the HTTP request

After calculating the signature, we have to add it to the request. We can add the signature to a request in one of two ways:

  • An HTTP header named Authorization
  • The query string
//Task 4: Add the signature to the HTTP request
String authorizationHeader = algorithm() + ' ' 
+ 'Credential=' + key + '/' 
+ credentialScope() + ', ' 
+  'SignedHeaders=' + signedHeaders() + ', ' 
+ 'Signature=' + calculatedSignature;

req.setHeader('Authorization',authorizationHeader);

It should be in the same format without any extra space and character.

Complete Code:

Note: Even space and any other character like / can create a different signatures. So please check extra space or another character if the signature is not generating properly.

Test using Custom API

Based on the above code we will get HTTPRequest with header and Authentication token. We can add other required parameters and send this request to AWS for processing.

Let us take an example, any mongo or .net API is hosted in AWS. This API should support AWS signature version 4 signing process. The following is API detail

Endpoint Url : https://patient.sysinfo.com/api/patient

Let us integrate the above custom API in Salesforce. The above API url should be added in Named Credential or added in the Remote site setting. Create custom metadata type AWSInfo to store AWS Authentication and other information. Add below fields in custom metadata AWSInfo.

Metadata Field NameMetadata API NameType
Host NameHostName__cText
ResourceResource__cText
KeyKey__cText
SecrateSecret__cText
RegionRegion__cText
ServiceService__cText

Store required AWS detail in this custom metadata type.

Apex Code:

This HTTPRequest is based on the above-mentioned URL. Please add the required parameter or other header parameter as per your requirement.

References:

Signing AWS API requests

Other Similar Posts:

Download S3 File using AWS Signature Version 4.0

Use Named Credential to Upload File in S3

View S3 File in Lightning Web Component

You may also like

5 comments

Anamika November 14, 2022 - 6:25 pm

Hi Dhanik,

Thanks for informative article.
AWSInfo__c must be AWSInfo__mdt in class ‘AWSService’. You have created metdata.

Reply
Dhanik Lal Sahni November 16, 2022 - 6:18 am

Yes Anamika, It is AWSInfo__mdt. Thank You for informing me.

Regards
Dhanik

Reply
Vaishnavi garg March 13, 2023 - 11:39 pm

Hi Dhanik,

Can you please add a screenshot of the custom metadata values you are using. What data should hostname__c and resource have?

Reply
Dhanik Lal Sahni March 14, 2023 - 9:27 pm

Hello Vaishnavi,
Host Name is an external API base URL like patient.sysinfo.com and the resource will be api/patient so the complete external URL will be patient.sysinfo.com/api/patient

Thank You,
Dhanik

Reply
Types Of Integration Patterns in Salesforce - SalesforceCodex June 12, 2023 - 9:16 am

[…] AWS Signature 4 Signing in Salesforce […]

Reply

Leave a Comment