腾讯API 签名方法 v3 Swift实现

直接上代码:

import Foundation
import CryptoKit

extension String {
    func SHA256() -> String {
        CryptoKit.SHA256.hash(data: self.data(using: .utf8)!)
            .compactMap { String(format: "%02x", $0) }
            .joined()
    }

    func HMAC_SHA256(_ key: String) -> HMAC<CryptoKit.SHA256>.MAC {
        HMAC<CryptoKit.SHA256>.authenticationCode(
            for : Data(self.utf8),
            using : SymmetricKey(data: Data(key.utf8)))
    }

    func HMAC_SHA256(_ key: HMAC<CryptoKit.SHA256>.MAC) -> HMAC<CryptoKit.SHA256>.MAC {
        HMAC<CryptoKit.SHA256>.authenticationCode(
            for : Data(self.utf8),
            using : SymmetricKey(data:key))
    }
}

extension HMAC<CryptoKit.SHA256>.MAC {
    func toString() -> String {
        self.compactMap { String(format: "%02x", $0) }
            .joined()
    }
}

struct TencentSecret {
    var secretId : String
    var secretKey : String
}

class TencentCloudRequest {

    enum Method : String {
        case GET = "GET"
        case POST =  "POST"
    }

    enum TencentCloudRequestError : Error {
        case RequestError(code:Int, data :String)
    }

    var headers : [String: String] = [:]

    var tencentSecret : TencentSecret
    var host : String
    var service : String
    var action : String
    var region : String
    var method : Method
    var payLoad : String
    var version : String
    var contentType : String

    var date : String = String(Date().formatted(.iso8601.year().month().day().dateSeparator(.dash)))
    var timestamp : String = "\(Int(Date().timeIntervalSince1970))"

    init(secret : TencentSecret ,
         host :String,
         service :String,
         action :String,
         region :String = "",
         method :Method = Method.POST,
         payload :String = "{}",
         version :String = "2020-03-24",
         contentType :String = "application/json"
    ) {
        self.tencentSecret = secret
        self.host = host
        self.service = service
        self.action = action
        self.region = region
        self.method = method
        self.payLoad = payload
        self.version = version
        self.contentType = contentType

        self.headers["Host"] = self.host
        self.headers["Content-Type"] = self.contentType
        self.headers["X-TC-Timestamp"] = self.timestamp
        self.headers["X-TC-Version"] = self.version
        self.headers["X-TC-Action"] = self.action
        if self.region != "" {
            self.headers["X-TC-Region"] = self.region
        }
    }
}

extension TencentCloudRequest {
    func sign() {
        var canonicalRequest = ""
        canonicalRequest.append(self.method.rawValue + "\n" )
        canonicalRequest.append("/\n" )
        canonicalRequest.append("\n")
        canonicalRequest.append("content-type:\(self.contentType)\nhost:\(self.host)\nx-tc-action:\(self.action.lowercased())\n" + "\n" )
        canonicalRequest.append("content-type;host;x-tc-action" + "\n" )
        canonicalRequest.append(self.payLoad.SHA256())

        var stringToSign = ""
        stringToSign.append("TC3-HMAC-SHA256" + "\n")
        stringToSign.append(self.timestamp + "\n")
        stringToSign.append("\(self.date)/\(self.service)/tc3_request" + "\n")
        stringToSign.append(canonicalRequest.SHA256())

        let secretDate = self.date.HMAC_SHA256("TC3" + self.tencentSecret.secretKey)
        let secretService = self.service.HMAC_SHA256(secretDate)
        let secretSigning = "tc3_request".HMAC_SHA256(secretService)
        let signature = stringToSign.HMAC_SHA256(secretSigning).toString()

        var authorization = ""
        authorization.append("TC3-HMAC-SHA256" + " ")
        authorization.append("Credential=" + self.tencentSecret.secretId + "/" + "\(self.date)/\(self.service)/tc3_request" + ", ")
        authorization.append("SignedHeaders=" + "content-type;host;x-tc-action" + ", " )
        authorization.append("Signature=" + signature)

        self.headers["Authorization"] = authorization
    }
}

extension TencentCloudRequest {
    func request() async throws -> Data {

        self.sign()

        let url : URL = URL(string: "https://\(self.headers["Host"]!)")!
        var request = URLRequest(url: url)
        request.httpMethod = self.method.rawValue
        self.headers.forEach { (key: String, value: String) in
            request.addValue(value, forHTTPHeaderField: key)
        }
        request.httpBody = self.payLoad.data(using: .utf8)

        let (data, response) = try await URLSession.shared.data(for: request)

        if let resp = response as? HTTPURLResponse {
            if resp.statusCode != 200 {
                throw TencentCloudRequestError.RequestError(code: resp.statusCode, data: String(data: data, encoding: String.Encoding.utf8)!)
            }
        }

        return data
    }
}

使用:

let request = TencentCloudRequest(
    secret: TencentSecret(secretId: "<secretId>",
                          secretKey: "<secretKey>"),
    host: "lighthouse.tencentcloudapi.com",
    service: "lighthouse",
    action: "DescribeRegions"
)

Task {
    let res = try? await request.request()
    print(String(data: res!, encoding: .utf8)!)
}

Leave a Comment

Back to Top