Signing Amazon REST Requests with SHA-256 in Swift
Accessing an Amazon REST API requires an encrypted request call, encoded with your secret key.
We’ll walk through an example with all the code you need to make it work.
Take this ItemLookup request of Amazon’s Product Advertising API:
http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE
&Operation=ItemLookup&ItemId=0679722769&ResponseGroup=
ItemAttributes,Offers,Images,Reviews&Version=2013-08-01
Breaking it down we have the following parameters:
- Service = AWSECommerceService
- AWSAccessKeyId = AKIAIOSFODNN7EXAMPLE
- Operation = ItemLookup
- ItemId = 0679722769
- ResponseGroup = ItemAttributes,Offers,Images,Reviews
- Version = 2013-08-01
- Timestamp = 2014-08-18T12:00:00Z
Step 1
Re-ordering all parameters by their key in order of ASCII character weight. This is essentially alphabetically, however all capitalized letters come before any lowercase letters.
Placing your parameters into an Array of Strings, the following snippet will reorder them correctly:
Step 2
Rejoin the array of parameters with ampersands into one canonical string:
Prepend the following three lines (with line breaks) before the canonical string:
Step 3
This string must now be encoded into an RFC 2104-compliant HMAC with the SHA256 hash algorithm, and then used to sign our request.
For the signing process, we’ll use CryptoSwift, a pure swift library containing many of the standard and secure cryptographic algorithms we’ll require when using APIs. Install CryptoSwift into your project using their documentation.
Step 4
Now that we have the CryptoSwift framework, we can create a class to use it. Our class’s main function will take the string from above, along with our Amazon Secret Key and return a signed 64Base value.
import Foundation
import CryptoSwift
class URLRequestSigner: NSObject {
func signString(stringToSign: String, secretSigningKey: String) -> String? {
guard let signature = try? HMAC(key: [UInt8](secretSigningKey.utf8),
variant: .sha256).authenticate([UInt8](stringToSign.utf8))
else { return .none }
return signature.toBase64()
}
}
//Call like this:
let signedString = URLRequestSigner().signString(stringToSign: stringToSign,
secretSigningKey: secretAccessKey)
Step 5
Add the signature to your request, and the result is a properly-formatted signed request:
http://webservices.amazon.com/onca/xml?
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE
&AssociateTag=mytag-20
&ItemId=0679722769
&Operation=ItemLookup
&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews
&Service=AWSECommerceService
&Timestamp=2014-08-18T12%3A00%3A00Z
&Version=2013-08-01
&Signature=j7bZM0LXZ9eXeZruTqWm2DIvDYVUU3wxPPpp%2BiXxzQc%3D
Step 6
We can now perform a request as usual:
let url = completeURLWithSignature
var request = URLRequest(url: url)
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request, completionHandler: { (data: Data?, response:
URLResponse?, error: Error?) -> Void in
print(data)
print(response)
print(error)
})
task.resume()
session.finishTasksAndInvalidate()
Congratulations you can now sign Amazon REST API requests!