// // DefaultSigV4.swift // AmazonChimeSDKMessagingDemo // // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 // import AWSCore let X_AMZ_ALGORITHM: String = "X-Amz-Algorithm" let X_AMZ_CREDENTIAL: String = "X-Amz-Credential" let X_AMZ_DATE: String = "X-Amz-Date" let X_AMZ_EXPIRES: String = "X-Amz-Expires" let X_AMZ_SIGNED_HEADERS: String = "X-Amz-SignedHeaders" let X_AMZ_SECURITY_TOKEN: String = "X-Amz-Security-Token" public class DefaultSigV4: SigV4 { private var credentials: ChimeUserCredentials private var region: String init(credentials: ChimeUserCredentials, region: String) { self.credentials = credentials self.region = region } public func signUrl(method: String, scheme: String, serviceName: String, hostname: String, path: String, payload: String, queryParams: [String: String]) -> String { let now = getDateTimeString() let today = getDateString(dateTimeString: now) let algorithm = "AWS4-HMAC-SHA256" let signedHeaders = "host" let canonicalHeaders = "host:\(hostname.lowercased())\n" let credentialScope = "\(today)/\(region)/\(serviceName)/aws4_request" var params: [String : String] = [ X_AMZ_ALGORITHM: algorithm, X_AMZ_CREDENTIAL: encodeURI(content: "\(credentials.accessKeyId)/\(credentialScope)"), X_AMZ_DATE: now, X_AMZ_EXPIRES: "10", X_AMZ_SIGNED_HEADERS: signedHeaders, X_AMZ_SECURITY_TOKEN: encodeURI(content: credentials.sessionToken) ] if !queryParams.isEmpty { for (key, value) in queryParams { params[key] = encodeURI(content: value) } } let sorted = params.sorted(by: { $0.0 < $1.0 }) var canonicalQueryString = "" for (key, value) in sorted { if !canonicalQueryString.isEmpty { canonicalQueryString.append("&") } canonicalQueryString.append(key) canonicalQueryString.append("=") canonicalQueryString.append(value) } let canonicalRequest = method + "\n" + path + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + sha256(content: payload) let hashedCanonicalRequest: String = sha256(content: canonicalRequest) let stringToSign = algorithm + "\n" + now + "\n" + credentialScope + "\n" + hashedCanonicalRequest let signingKey = getSignatureKey(key: credentials.secretAccessKey, date: today, regionName: region, serviceName: serviceName) let signature = hmac(data: stringToSign, key: signingKey).hexEncodedString() let finalParams = "\(canonicalQueryString)&X-Amz-Signature=\(signature)" return "\(scheme)://\(hostname)\(path)?\(finalParams)" } private func getDateTimeString() -> String { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'" dateFormatter.timeZone = (NSTimeZone(name: "UTC")! as TimeZone) dateFormatter.locale = Locale(identifier: Locale.current.languageCode!) return dateFormatter.string(from: Date()) } private func getDateString(dateTimeString: String) -> String { return String(dateTimeString[.. String { let allowedCharacterSet = CharacterSet(charactersIn: "!*'();:@&=+$,/?%#[] ").inverted return content.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet)! } private func sha256(content: String) -> String { return AWSSignatureSignerUtility.hexEncode(AWSSignatureSignerUtility.hashString(content)) } private func getSignatureKey(key: String, date: String, regionName: String, serviceName: String) -> Data { let kSecret = Data("AWS4\(key)".utf8) let kDate = hmac(data: date, key: kSecret) let kRegion = hmac(data: regionName, key: kDate) let kService = hmac(data: serviceName, key: kRegion) let kSigning = hmac(data: "aws4_request", key: kService) return kSigning } func hmac(data: String, key: Data) -> Data { return AWSSignatureSignerUtility.sha256HMac(with: Data(data.utf8), withKey: key) } }