AWS Signature Version 4 Signing Process

Document created by sheng_liao462475 Employee on Oct 19, 2017Last modified by sheng_liao462475 Employee on Nov 8, 2017
Version 3Show Document
  • View in full screen mode

This article describes how to generate AWS signature version 4 and add it to the web service call request.

 

Use Case


When you manually create HTTP requests to AWS EC2, you must sign the requests by using AWS signature version 4.

 

Approach

1. Build a Canonical Request for Signature Version 4

 

To create a canonical request, concatenate the following components into a single string:

  • Start with the HTTP request method (GET, PUT, POST, etc.), followed by a newline character.
  • Add the canonical URI parameter, followed by a newline character.
  • Add the canonical query string, followed by a newline character. If the request does not include a query string, use an empty string (essentially, a blank line).
  • Add the canonical headers, followed by a newline character.
  • Add the signed headers, followed by a newline character. This value is the list of headers that you included in the canonical headers. By adding this list of headers, you tell AWS which headers in the request are part of the signing process and which ones AWS can ignore. Use hash function SHA256 to create a hashed value from the payload in the body of the HTTP or HTTPS request.
  • To construct the finished canonical request, combine all the components from each step as a single string. 

 

2. Create a String to Sign for Signature Version 4

 

  • To create the string to sign, start with the algorithm designation, followed by a newline character. This value is the hashing algorithm that you use to calculate the digests in the canonical request. For SHA256, AWS4-HMAC-SHA256 is the algorithm.
  • Append the request date value, followed by a newline character. The date is specified with ISO8601 basic format in the x-amz-date header in the format YYYYMMDD'T'HHMMSS'Z'. This value must match the value you used in any previous steps.
  • Append the credential scope value, followed by a newline character. This value is a string that includes the date, the region you are targeting, the service you are requesting, and a termination string ("aws4_request") in lowercase characters. The region and service name strings must be UTF-8 encoded.
  • Use hash function SHA256 to create a hashed value from the canonical request. This value is not followed by a newline character. The hashed canonical request must be lowercase base-16 encoded.

 

3. Calculate the Signature for AWS Signature Version 4

 

To calculate a signature, use your secret access key to create a series of hash-based message authentication codes (HMACs).
Pseudocode for deriving a signing key:

kSecret = your secret access key
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")

Use the signing key that you derived and the string to sign as inputs to the keyed hash function. After you calculate the signature, convert the binary value to a hexadecimal representation.


4. Add the Signing Information to the Request

You can pass signing information either through the Authorization Header or through Query String, but you cannot pass it through both Authorization Header and Query String.

 

Implementation

1. Define 6 Dynamic Process Properties.

  • Payload - The payload you are sending. This can be empty.
  • Date - Current Date. Date needs to be in UTC time zone and the format needs to be yyyyMMdd. 
  • Region - The region you are targeting.
  • Service - The service you are requesting.
  • Access_Key_ID - Your AWS access key.
  • SecretKey - Your AWS secret key.

Below is the script to create canonical request and calculate String to Sign.

import java.util.Properties;
import java.util.Calendar;
import java.text.SimpleDateFormat;
import java.io.InputStream;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import com.boomi.execution.ExecutionUtil;
import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
public class SignString{

public static byte[] HmacSHA256(byte[] key) {
MessageDigest mac = MessageDigest.getInstance("SHA-256");
byte[] signatureBytes = mac.digest(key);
return signatureBytes;
}
public static String convertbyte(byte[] bytes) {
StringBuffer hexString = new StringBuffer();
for (int j=0; j<bytes.length; j++) {
String hex=Integer.toHexString(0xff & bytes[j]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
for( int i = 0; i < dataContext.getDataCount(); i++ ) {
InputStream is = dataContext.getStream(i);
Properties props = dataContext.getProperties(i);
// Acquire applicable Properties
Day = ExecutionUtil.getDynamicProcessProperty("Date");
Input = ExecutionUtil.getDynamicProcessProperty("Payload");
Region = ExecutionUtil.getDynamicProcessProperty("Region");
Service = ExecutionUtil.getDynamicProcessProperty("Service");
AccessKey = ExecutionUtil.getDynamicProcessProperty("Access_Key_ID");
version = version number;
Date now= new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
TimeZone utc = TimeZone.getTimeZone("UTC");
sdf.setTimeZone(utc);
CDT = (sdf.format(now)).toString();
// Build CanonicalHeaders inputs for CanonicalRequest
CanonicalHeaders_line1 = "content-type:application/x-www-form-urlencoded";
CanonicalHeaders_line2 = "host:ec2.amazonaws.com";
CanonicalHeaders_line3 = "x-amz-date:"+CDT;
// Build CanonicalRequest
Request_Method = "GET";
CanonicalURI = "/";
CanonicalQueryString = "Action=DescribeRegions&Version=version";
CanonicalHeaders = CanonicalHeaders_line1 + "\n" + CanonicalHeaders_line2 + "\n" + CanonicalHeaders_line3 + "\n";
SignedHeaders_line = "content-type;host;x-amz-date";
byte[] HashedPayload_bytes = new SignString().HmacSHA256(Input.getBytes("UTF-8"));
HashedPayload = new SignString().convertbyte(HashedPayload_bytes);
CanonicalRequest = Request_Method + "\n" + CanonicalURI + "\n" + CanonicalQueryString + "\n" + CanonicalHeaders + "\n" + SignedHeaders_line + "\n" + HashedPayload;
// Calculate String to sign
Signing_algorithm = "AWS4-HMAC-SHA256";
RequestDate = CDT;
CredentialScope = Day+"/"+Region+"/"+Service+"/aws4_request";
byte[] HashedCanonicalRequest_bytes = new SignString().HmacSHA256(CanonicalRequest.getBytes("UTF-8"));
HashedCanonicalRequest = new SignString().convertbyte(HashedCanonicalRequest_bytes);
string_to_sign = Signing_algorithm+"\n"+RequestDate+"\n"+CredentialScope+"\n"+HashedCanonicalRequest;
ExecutionUtil.setDynamicProcessProperty("String_to_sign",string_to_sign, false);
dataContext.storeStream(is, props);
}

 

2. Use the below script to calculate signature and store the signature in a dynamic process property.

import java.util.Properties;
import java.io.InputStream;
import java.lang.Byte;
import com.boomi.execution.ExecutionUtil;
import javax.xml.bind.DatatypeConverter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import java.security.MessageDigest;
String String_to_sign= ExecutionUtil.getDynamicProcessProperty("String_to_sign");
Day = ExecutionUtil.getDynamicProcessProperty("Date");
Region = ExecutionUtil.getDynamicProcessProperty("Region");
Service = ExecutionUtil.getDynamicProcessProperty("Service");
String secret_key = ExecutionUtil.getDynamicProcessProperty("SecretKey");
// Create a signing key.
byte[] signing_key = new CalculateSignature().getSignatureKey(secret_key, Day, Region, Service);
// Use the signing key to sign the StringToSign using HMAC-SHA256 signing algorithm.
byte[] signature_bytes = new CalculateSignature().HmacSHA256(String_to_sign, signing_key);
String signature = new CalculateSignature().convertbyte(signature_bytes);
String signature1 = new CalculateSignature().convertbyte(signing_key);
public class CalculateSignature{
public static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) {
byte[] kSecret = ("AWS4" + key).getBytes("utf-8");
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
public static byte[] HmacSHA256(String data, byte[] key) {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256")
mac.init(secretKeySpec);
return mac.doFinal(data.getBytes("utf-8"));
}

public static String convertbyte(byte[] bytes) {
StringBuffer hexString = new StringBuffer();
for (int j=0; j<bytes.length; j++) {
String hex=Integer.toHexString(0xff & bytes[j]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
ExecutionUtil.setDynamicProcessProperty("Signature", signature, false);
for( int i = 0; i < dataContext.getDataCount(); i++ ) {
InputStream is = dataContext.getStream(i);
Properties props = dataContext.getProperties(i);
dataContext.storeStream(is, props);
}

 

3. Use a message shape to construct the Authorization. Below is an example.

AWS4-HMAC-SHA256 Credential={1}/{2}/{3}/{4}/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature={5}

 

{1} - Dynamic Process Property: Access_Key_ID

{2} - Dynamic Process Property: Date

{3} - Dynamic Process Property: Region

{4} - Dynamic Process Property: Service

{5} - Dynamic Process Property: Signature

Then assign "Current Data" to a Dynamic Document Property - Authorization.

Add the following to HTTP Request Headers:

Authorization

X-Amz-Date

Host

Content-Type

Add the following to Resource Path:

?Action=

ActionName

&Version=

version number

Please note that you will also need to add the version number to the resource path, otherwise you may get error message "Invalid Action: The Action <actionname> is not valid for this web service".

 

More Information

Task 3: Calculate the Signature for AWS Signature Version 4 - Amazon Web Services 

Attachments

    Outcomes