Amazon Web Services General Reference
  (Version 1.0)
« PreviousNext »
Go to the AWS Discussion Forum for this product.Did this page help you?  Yes | No |  Tell us about it...

Signature Version 4 Key Derivation Samples

Errors in implementing the key derivation algorithm are difficult to troubleshoot. For security reasons, AWS services report only a signature mismatch in response to an error—not the expected result. To simplify your task, you can use the following code examples to implement the derivation in several widely used programming languages.

Deriving the signing key using Java

static byte[] HmacSHA256(String data, byte[] key) throws Exception  {
     String algorithm="HmacSHA256";
     Mac mac = Mac.getInstance(algorithm);
     mac.init(new SecretKeySpec(key, algorithm));
     return mac.doFinal(data.getBytes("UTF8"));
}

static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception  {
     byte[] kSecret = ("AWS4" + key).getBytes("UTF8");
     byte[] kDate    = HmacSHA256(dateStamp, kSecret);
     byte[] kRegion  = HmacSHA256(regionName, kDate);
     byte[] kService = HmacSHA256(serviceName, kRegion);
     byte[] kSigning = HmacSHA256("aws4_request", kService);
     return kSigning;
}

Deriving the signing key using .NET

static byte[] HmacSHA256(String data, byte[] key)
{
    String algorithm = "HmacSHA256";
    KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
    kha.Key = key;

    return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}

static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
    byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
    byte[] kDate = HmacSHA256(dateStamp, kSecret);
    byte[] kRegion = HmacSHA256(regionName, kDate);
    byte[] kService = HmacSHA256(serviceName, kRegion);
    byte[] kSigning = HmacSHA256("aws4_request", kService);
 
    return kSigning;
}

Deriving the signing key using Python

def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()

def getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = sign(("AWS4" + key).encode("utf-8"), dateStamp)
    kRegion = sign(kDate, regionName)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, "aws4_request")
    return kSigning

Deriving the signing key using Ruby

def getSignatureKey key, dateStamp, regionName, serviceName
    kDate    = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
    kRegion  = OpenSSL::HMAC.digest('sha256', kDate, regionName)
    kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
    kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")

    kSigning
end

Deriving the signing key using JavaScript

For more information about Crypto-JS and HMAC, go to http://code.google.com/p/crypto-js/#HMAC.

function getSignatureKey(key, dateStamp, regionName, serviceName) {

   var kDate= Crypto.HMAC(Crypto.SHA256, dateStamp, "AWS4" + key, { asBytes: true})
   var kRegion= Crypto.HMAC(Crypto.SHA256, regionName, kDate, { asBytes: true });
   var kService=Crypto.HMAC(Crypto.SHA256, serviceName, kRegion, { asBytes: true });
   var kSigning= Crypto.HMAC(Crypto.SHA256, "aws4_request", kService, { asBytes: true });

   return kSigning;
}

Important

Use the asBytes option when calling Crypto.HMAC in JScript or JavaScript. Otherwise, the HMAC implementation will perform an additional encoding by default.

Deriving the signing key using other languages

If you need to implement this logic in a different programming language, we recommend testing against the reference code. The example below prints the results using the following Ruby hexEncode function after each step in the algorithm.

def hexEncode bindata
  result=""
  data=bindata.unpack("C*")
  data.each {|b| result+= "%02x" % b}
  result
end

Given the following test input:

key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'

Your program should generate the following values for the values in getSignatureKey. These are hex-encoded representations of the binary data.

kSecret  = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate    = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion  = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'

Common Coding Mistakes

During development and testing of Signature Version 4, we came across a number of coding errors we share below in the hope of simplifying your task.

  • Inadvertently swapping the key and the data when calculating intermediary keys. The result of the previous step's computation is the key, not the data. Check the documentation for your cryptographic primitives carefully to ensure that you place the parameters in the proper order.

  • Forgetting to prepend the string "AWS" in front of the key for the first step. It is possible to implement the key derivation using a for loop or iterator. When you do, don't forget to special-case the first iteration so that it includes the "AWS" string.

  • Forgetting to use the asBytes option for the JavaScript HMAC.Crypto function. If you don't use the asBytes option, the HMAC implementation will perform an additional hex encoding by default.

  • Taking a query parameter from within a URL-encoded request like the POST request in Signed Signature Version 4 Requests Examples to use as a query parameter string in the canonical query string. The parameters that you use as part of your canonical query string cannot come from the body of this type of request.