I have a create a class for GET request named is AWSS3RequestSignerGET. Also create String and Data extension for generating byte array. All code given below. if you want to run this code need to import CommonCrypto. This request working fine with PUT but not working with GET.
I have removed only body part in GET request.I am using same accessKeyId and secretAccessKey which is used in PUT request but getting below error
{"message":"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."}
class AWSS3RequestSignerGET: NSObject {
private let hmacShaTypeString = "AWS4-HMAC-SHA256"
private let awsRegion = "us-west-1"
private let serviceType = "es"
private let aws4Request = "aws4_request"
private let iso8601Formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .iso8601)
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyyMMdd'T'HHmmssXXXXX"
return formatter
}()
private func iso8601() -> (full: String, short: String) {
let date = iso8601Formatter.string(from: Date())
let index = date.index(date.startIndex, offsetBy: 8)
let shortDate = date.substring(to: index)
return (full: date, short: shortDate)
}
func signGET(request: URLRequest, secretSigningKey: String, accessKeyId: String) -> URLRequest? {
var signedRequest = request
let date = iso8601()
guard let url = signedRequest.url, let host = url.host
else { return .none }
signedRequest.addValue(host, forHTTPHeaderField: "Host")
signedRequest.addValue(date.full, forHTTPHeaderField: "X-Amz-Date")
guard let headers = signedRequest.allHTTPHeaderFields, let method = signedRequest.httpMethod
else { return .none }
let signedHeaders = headers.map{ $0.key.lowercased() }.sorted().joined(separator: ";")
let canonicalRequestHash = [
method,
url.path,
url.query ?? "",
headers.map{ $0.key.lowercased() + ":" + $0.value }.sorted().joined(separator: "\n"),
"",
signedHeaders
].joined(separator: "\n").sha256()
let credential = [date.short, awsRegion, serviceType, aws4Request].joined(separator: "/")
let stringToSign = [
hmacShaTypeString,
date.full,
credential,
canonicalRequestHash
].joined(separator: "\n")
guard let signature = signatureWith(stringToSign: stringToSign, secretAccessKey: secretSigningKey, shortDateString: date.short)
else { return nil }
let authorization = hmacShaTypeString + " Credential=" + accessKeyId + "/" + credential + ", SignedHeaders=" + signedHeaders + ", Signature=" + signature
signedRequest.addValue(authorization, forHTTPHeaderField: "Authorization")
return signedRequest
}
private func getCredential(date: String, accessKeyId: String) -> String {
let credential = [date, awsRegion, serviceType, aws4Request].joined(separator: "/")
return credential
}
/*
DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
*/
private func signatureWith(stringToSign: String, secretAccessKey: String, shortDateString: String) -> String? {
let firstKey = "AWS4" + secretAccessKey
let dateKey = shortDateString.hmac(keyString: firstKey)
let dateRegionKey = awsRegion.hmac(keyData: dateKey)
let dateRegionServiceKey = serviceType.hmac(keyData: dateRegionKey)
let signingKey = aws4Request.hmac(keyData: dateRegionServiceKey)
let signature = stringToSign.hmac(keyData: signingKey)
return signature.toHexString()
}
}
extension String {
func sha256() -> String {
guard let data = self.data(using: .utf8) else { return "" }
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
let outputData = Data(bytes: hash)
return outputData.toHexString()
}
func hmac(keyString: String) -> Data {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyString, keyString.count, self, self.count, &digest)
let data = Data(bytes: digest)
return data
}
func hmac(keyData: Data) -> Data {
let keyBytes = keyData.bytes()
let data = self.cString(using: String.Encoding.utf8)
let dataLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
var result = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyBytes, keyData.count, data, dataLen, &result);
return Data(bytes: result)
}
}
extension Data {
func toHexString() -> String {
let hexString = self.map{ String(format:"%02x", $0) }.joined()
return hexString
}
func bytes() -> [UInt8] {
let array = [UInt8](self)
return array
}
}