Python可用的加密passphrase在Haskell请求KuCoin API时无效
KuCoin期货API签名认证passphrase校验失败问题排查
问题现象
向KuCoin期货API发起携带签名头的GET请求时,所有签名计算逻辑与可正常运行的Python代码对齐,但接口返回{"code":"400004","msg":"Invalid KC-API-PASSPHRASE"}错误。
- 其余签名相关字段(
KC-API-SIGN、KC-API-TIMESTAMP、KC-API-KEY)校验均正常 - 已尝试添加BangPatterns扩展排除惰性求值影响,问题未解决
- 两端生成的passphrase打印值对比如下:
- Python输出:
b'e3GO2zT3kWv1oVrAhAF1TByq0u8ExNMw/76KrsFGnls=' - Haskell输出:
e3GO2zT3kWv1oVrAhAF1TByq0u8ExNMw/76KrsFGnls=
注:Python输出的
b''是字节串类型的打印标记,两端实际生成的passphrase字符串内容完全一致,计算逻辑无错误。 - Python输出:
参考实现(可正常运行的Python代码)
api_passphrase = "passphrase" api_secret = "secret" api_key = "key" url = 'https://api-futures.kucoin.com/api/v1/position?symbol=XBTUSDM' now = int(time.time() * 1000) str_to_sign = str(now) + 'GET' + '/api/v1/position?symbol=XBTUSDM' signature = base64.b64encode( hmac.new(api_secret.encode('utf-8'), str_to_sign.encode('utf-8'), hashlib.sha256).digest()) passphrase = base64.b64encode(hmac.new(api_secret.encode('utf-8'), api_passphrase.encode('utf-8'), hashlib.sha256).digest()) headers = { "KC-API-SIGN": signature, "KC-API-TIMESTAMP": str(now), "KC-API-KEY": api_key, "KC-API-PASSPHRASE": passphrase, "KC-API-KEY-VERSION": "2" } response = requests.request('get', url, headers=headers) print(passphrase)
异常实现(Haskell代码)
import qualified Config as C import Control.Lens import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class import qualified Crypto.Hash.SHA256 as H import qualified Data.Aeson as A import Data.Aeson.Lens as AL import qualified Data.ByteString as B import qualified Data.ByteString.Base64 as U import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.UTF8 as BU import Data.Monoid import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import qualified Data.Time.Clock.POSIX as TI import GHC.Generics import Network.Wreq apiSign :: BC.ByteString -> BC.ByteString -> BC.ByteString -> TI.POSIXTime -> IO BC.ByteString apiSign method endpoint body time = do timeStamp <- apiTimeStamp' time let message = mconcat [timeStamp,method,endpoint,body] let hash = H.hmac (BC.pack C.apiSecret) message return $ (encodeUtf8 . U.encodeBase64) hash apiTimeStamp' :: TI.POSIXTime -> IO BC.ByteString apiTimeStamp' = return . BC.take 13 . BC.pack . show . (* 1000) apiPassphrase :: BC.ByteString apiPassphrase = U.encodeBase64' $ H.hmac (BC.pack C.apiSecret) (BC.pack C.apiPassphrase) apiKeyVersion :: BC.ByteString apiKeyVersion = BC.pack C.apiVersion main' :: IO () main' = do time <- TI.getPOSIXTime timeStamp <- apiTimeStamp' time apiSignValue <- apiSign "GET" "/api/v1/position?symbol=XBTUSDM" "" time let opt = defaults & header "Accept" .~ ["*/*"] & header "KC-API-VERSION" .~ ["2"] & header "KC-API-KEY" .~ [BC.pack C.apiKey] & header "KC-API-TIMESTAMP" .~ [timeStamp] & header "KC-API-PASSPHRASE" .~ [apiPassphrase] & header "KC-API-SIGN" .~ [apiSignValue] & param "symbol" .~ ["XBTUSDM"] r <- getWith opt (kucoin ++ "position") let response = r ^. responseBody print response
根因分析
问题出在请求头名称拼写错误:
- Python正确代码中,标识密钥版本的请求头名为
KC-API-KEY-VERSION,值为2,代表使用V2版本签名逻辑,此时KC-API-PASSPHRASE需要传API密钥加密后再base64编码的值。 - Haskell代码中误将该请求头写为
KC-API-VERSION,服务端无法识别到V2版本标记,默认回退到V1版本签名校验逻辑,V1逻辑要求KC-API-PASSPHRASE传递明文的passphrase值,和传入的加密后值不匹配,因此返回passphrase无效的错误。
修复方案
修改Haskell代码中请求头的键名,将错误的KC-API-VERSION替换为正确的KC-API-KEY-VERSION即可,修改后的配置段如下:
let opt = defaults & header "Accept" .~ ["*/*"] -- 修正头名拼写 & header "KC-API-KEY-VERSION" .~ ["2"] & header "KC-API-KEY" .~ [BC.pack C.apiKey] & header "KC-API-TIMESTAMP" .~ [timeStamp] & header "KC-API-PASSPHRASE" .~ [apiPassphrase] & header "KC-API-SIGN" .~ [apiSignValue] & param "symbol" .~ ["XBTUSDM"]
其余签名计算、passphrase生成逻辑均正确,无需修改。
内容的提问来源于stack exchange,提问作者artin ghasivand
相关产品推荐
相关产品推荐

