You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中生成与Python一致的Kraken API HMAC-SHA512签名?

Kraken API HMAC-SHA512签名Swift与Python实现不一致问题

我尝试用Swift调用需要认证的Kraken API,参考官网的Python示例复刻代码,但生成的HMAC-SHA512签名和Python版本不一致。以下是相关代码:

Python示例代码(来自Kraken API文档)

import urllib.parse
import hashlib
import hmac
import base64

def get_kraken_signature(urlpath, data, secret):

    postdata = urllib.parse.urlencode(data)
    encoded = (str(data['nonce']) + postdata).encode()
    message = urlpath.encode() + hashlib.sha256(encoded).digest()

    mac = hmac.new(base64.b64decode(secret), message, hashlib.sha512)
    sigdigest = base64.b64encode(mac.digest())
    return sigdigest.decode()

api_sec = "kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg=="

data = {
    "nonce": "1616492376594",
    "ordertype": "limit",
    "pair": "XBTUSD",
    "price": 37500,
    "type": "buy",
    "volume": 1.25
}

signature = get_kraken_signature("/0/private/AddOrder", data, api_sec)
print("API-Sign: {}".format(signature))
#Output: 4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==
#This output is the expected output, as Kraken states on their website

我的Swift代码

import CryptoKit
func encryptMsg() -> String {
    let privateKey = "kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg=="
    let payload = "1616492376594nonce=1616492376594&ordertype=limit&pair=XBTUSD&price=37500&type=buy&volume=1.25"
    let apiPath = "/0/private/AddOrder"
    var payload2 = apiPath
    
    // Sha256Digest
    let sha256Digest: SHA256Digest = SHA256.hash(data: Data(payload.utf8))
    debugPrint(sha256Digest)
    // Output: 23a1c1b34c6a11d641af0f24684896cb90f66fb991125c83dc357bdc3dc146f1
    
    // Convert sha256Digest from hex to ascii
    for element in sha256Digest {
        payload2.append(Character(UnicodeScalar(Int(element.description)!)!))
    }
    debugPrint(payload2)
    // Swift : /0/private/AddOrder#¡Á³Lj\u{11}ÖA¯\u{0F}$hHËöo¹\u{12}\\Ü5{Ü=ÁFñ
    // Python: /0/private/AddOrder#\xa1\xc1\xb3Lj\x11\xd6A\xaf\x0f$hH\x96\xcb\x90\xf6o\xb9\x91\x12\\\x83\xdc5{\xdc=\xc1F\xf1
    // Swift output identical to Python output
    
    // Private key
    debugPrint(Data(base64Encoded: privateKey)!.map { String(format: "%02x", $0) }.joined())
    // Swift : 9101f91d6ffca75b863958db81603b16e9c09863bc96c4945cdb2eddea30efab33f38435f1f5b19f247304709dde97799c4f6a6bdf47019b6e66e8fa17586e5e
    // Python: \x91\x01\xf9\x1do\xfca7[\x869X\xdb\x81`;\x16\xe9\xc0\x98c\xbc\x96\xc4\x94\\\xdb.\xdd\xea0\xef\ab3\xf3\x845\xf1\xf5\xb1\x9f$s\x04p\x9d\xde\x97y\x9cOjk\xdfG\x01\x9bnf\xe8\xfa\x17Xn^
    // Swift output identical to Python output
    let key = SymmetricKey(data: Data(base64Encoded: privateKey)!)
    
    // SHA512 digest and Hmac construction
    var hmacSHA512 = HMAC<SHA512>.authenticationCode(for: Data(payload2.utf8), using: key)
    debugPrint(hmacSHA512)
    // Swift : e82d03bb76af28c9bb55dfc460bf8a0f64d30d0e55307bc5236f5d0581bf8864bce237238d4b7cc3c832a7e2545b1d0f70794545ce76d1bd656c13b054ea54da
    // Python: \xe3\xf7i\xc5\xbd\xe2O\x8bi\xfd\x90\x95\x13\x04\xa7\x12\xc2\xf1\xc7F\xea\xca\x12\xe9u\xf3\xa9s\xa7\xe7\xec\xe4|\xf9@\xa5I^g\xf4N\x9aI/\x0c>\xd9\xd1~\x9d\xf6l\x06\xf4\x9ef\xd1\x9f\xa1\xfc\x9d\xdc\x0bQ
    // Swift output different from Python output
    
    let finalSignature = Data(hmacSHA512).base64EncodedString()
    debugPrint(finalSignature)
    // Swift : 6C0Du3avKMm7Vd/EYL+KD2TTDQ5VMHvFI29dBYG/iGS84jcjjUt8w8gyp+JUWx0PcHlFRc520b1lbBOwVOpU2g==
    // Python: 4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==
    // Final result: Swift output different from Python output
    
    return finalSignature
}

请问我哪里出错了?


问题分析与修正

你的错误出在构造HMAC输入消息的方式上:Python代码是直接将urlpath的字节数据与SHA256的原始字节摘要拼接,而你的Swift代码却把SHA256摘要转成了Unicode字符再拼接到字符串里,最后又转成UTF-8字节——这个过程中,部分不可打印的ASCII字符会被错误处理,导致最终的消息字节流和Python不一致。

正确的Swift实现

import CryptoKit

func getKrakenSignature(urlPath: String, data: [String: Any], secret: String) -> String? {
    // 1. 编码post数据,和Python的urlencode逻辑对齐
    guard let postData = data.urlEncodedString().data(using: .utf8),
          let nonce = data["nonce"] as? String,
          let secretData = Data(base64Encoded: secret),
          let urlPathData = urlPath.data(using: .utf8) else {
        return nil
    }
    
    // 2. 构造encoded:nonce + postData的字节拼接
    let encoded = nonce.data(using: .utf8)! + postData
    
    // 3. 计算SHA256摘要
    let sha256Digest = SHA256.hash(data: encoded)
    
    // 4. 构造HMAC的输入消息:urlPath字节 + SHA256原始字节摘要
    let message = urlPathData + Data(sha256Digest)
    
    // 5. 计算HMAC-SHA512
    let key = SymmetricKey(data: secretData)
    let hmacDigest = HMAC<SHA512>.authenticationCode(for: message, using: key)
    
    // 6. 转成Base64字符串
    return Data(hmacDigest).base64EncodedString()
}

// 辅助方法:模拟Python的urlencode逻辑,处理字典转URL编码字符串
extension Dictionary where Key == String, Value == Any {
    func urlEncodedString() -> String {
        return self.compactMap { key, value in
            guard let encodedKey = key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
                  let encodedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
                return nil
            }
            return "\(encodedKey)=\(encodedValue)"
        }.joined(separator: "&")
    }
}

// 测试调用
let apiSec = "kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg=="
let data: [String: Any] = [
    "nonce": "1616492376594",
    "ordertype": "limit",
    "pair": "XBTUSD",
    "price": 37500,
    "type": "buy",
    "volume": 1.25
]

if let signature = getKrakenSignature(urlPath: "/0/private/AddOrder", data: data, secret: apiSec) {
    print("API-Sign: \(signature)")
    // 输出会和Python一致:4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ==
}

关键修正点

  • 直接操作字节数据:不再把SHA256摘要转成字符串,而是直接用Data(sha256Digest)获取原始字节,和urlPath的字节拼接成HMAC的输入消息,和Python逻辑完全对齐。
  • 模拟Python的urlencode:新增了字典转URL编码字符串的辅助方法,确保post数据的编码结果和Python一致(比如处理数字、字符串的转义)。
  • 避免字符串转义问题:全程用Data处理字节流,杜绝Unicode字符转义带来的字节不一致问题。

内容的提问来源于stack exchange,提问作者Marcel Winters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 06:24:53