iOS AWS Request 4

유호준·2022년 4월 18일
0
post-thumbnail

iOS로 AWS Request 4 요청을 보내는 게 생각보다 쉽지 않아 정리해보기로 했다.

PUT test$file.text HTTP/1.1
Host: examplebucket.s3.amazonaws.com
Date: Fri, 24 May 2013 00:00:00 GMT
Authorization: SignatureToBeCalculated
x-amz-date: 20130524T000000Z 
x-amz-storage-class: REDUCED_REDUNDANCY
x-amz-content-sha256: 44ce7dd67c959e0d3524ffac1771dfbba87d2b6b4b4e99e42034a8b803f8b072

<Payload>

많은 과정이 있지만 결국 해야되는 것은 위와 같이 header를 만들어주는 것이다.

Date

먼저 Date형식부터 위 같이 맞춰줘야 하는데, 이때 Date의 timezone을 UTC시간으로 설정해야한다.
Date는 x-amz-date에 필요한 형식과 다른 timestamp형식이 하나 더 필요한데 이는 index를 이용해서 잘라주었다.

private var dateString:String{
        switch self {
        case .saveFile:
            let dateFormatter =  DateFormatter()
            dateFormatter.dateFormat = "yyyyMMdd'T'HHmmssXXXXX"
            dateFormatter.locale = Locale(identifier: "en_US_POSIX")
            dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
            return dateFormatter.string(from: Date())
        }
    }
private var timestampString:String{
	let index = dateString.index(dateString.startIndex, offsetBy: 7)
    return String(dateString[...index])
}

Payload

PUT연산을 할때는 Payloadhashing해서, header에 넣어줘야 한다.

 private var hashedPayload: String{
        switch self {
        case .saveFile(let url):
            do{
                let payload = try Data(contentsOf:url)
                return SHA256.hash(data: payload).map{String(format: "%02hhx", $0)}.joined()
            }catch{
                fatalError("파일 hash 과정 중 오류 발생: " + error.localizedDescription)
            }
        }
    }

CanonicalRequest

그 다음에는 CanonicalRequest를 문자열로 생성해준다.

let canonicalHeaders = """
content-type:application/zip\n\
host:\(host)\n\
x-amz-content-sha256:\(hashedPayload)\n\
x-amz-date:\(dateString)
"""
let signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date"
let canonicalRequest = "PUT\n/\(endPoint)\n\n\(canonicalHeaders)\n\n\(signedHeaders)\n\(hashedPayload)"

이때 signedHeaders의 순서는 알파벳 순서이다.

StringToSign

stringToSignCanonicalRequesthashing해서 형식을 맞춰준다.

let credentialScope:String = timestampString + "/" + region + "/" + service + "/" + "aws4_request"    
let stringToSign:String = algorithm + "\n" + dateString + "\n" + credentialScope + "\n" 
                          + SHA256.hash(data: canonicalRequest.data(using: .utf8)!).map{String(format: "%02hhx", $0)}.joined()
let algorithm = "AWS4-HMAC-SHA256"
let region = "ap-northeast-2"
let service = "s3"

Signature

드디어 signature를 생성할 준비가 끝났다. HMAC은 Swift의 CryptoKit을 사용하면 쉽게 가능하다.

private var signatureKey: SymmetricKey {
	guard let secretKey = Bundle.main.infoDictionary!["AWS_SECRET_KEY"] as? String else{
		fatalError("SECRET KEY 없음")
	}
	let kDate = HMAC<SHA256>.authenticationCode(for: timestampString.data(using: .utf8)!, using: SymmetricKey(data:("AWS4" + secretKey).data(using: .utf8)!))
	let kRegion = HMAC<SHA256>.authenticationCode(for: region.data(using: .utf8)!, using: SymmetricKey(data:kDate))
	let kService = HMAC<SHA256>.authenticationCode(for: service.data(using: .utf8)!, using: SymmetricKey(data:kRegion))
	return SymmetricKey(data:HMAC<SHA256>.authenticationCode(for: "aws4_request".data(using: .utf8)!, using: SymmetricKey(data:kService)))
 }
let signature = HMAC<SHA256>.authenticationCode(for: stringToSign.data(using: .utf8)!, using: signatureKey)
                .map{String(format: "%02hhx", $0)}.joined()

Authorization Header

마지막으로 Authorization Header를 아래와 같이 만들고 요청을 보내면 성공! 만약 안된다면 response body를 출력해보면 안되는 이유를 알 수 있다.

let awsSignature = """
\(algorithm) Credential=\(accessKey)/\(credentialScope), \
SignedHeaders=\(signedHeaders), Signature=\(signature)
"""
private var headers: HTTPHeaders{
	switch self {
    	case .saveFile:
        	return [
                "Content-Type":"application/zip",
                "Host":"\(host)",
                "X-Amz-Content-SHA256":hashedPayload,
                "X-Amz-Date":dateString,
                "Authorization":awsSignature
            ]
        }
  }

참고
https://gist.github.com/elmyn/b63913d7ba4ffa26b37d55c7b7e260e1
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
https://docs.aws.amazon.com/ko_kr/general/latest/gr/sigv4-signed-request-examples.html

0개의 댓글