如何签署 OKEx API 请求?

问题描述 投票:0回答:4

我在尝试向 OKEx API 进行身份验证时不断收到无效签名错误,但无法理解为什么我的签名没有通过。另一只眼睛可能有帮助吗?

以下是 OKEx API 文档中的一些上下文:

*---签名消息---

  1. OK-ACCESS-SIGN header生成如下:create a prehash 时间戳字符串 + 方法 + requestPath + 正文(其中 + 代表字符串连接)准备 Secret 标志 prehash 使用 HMAC SHA256 将签名编码为 Secret 的字符串 Base64 格式示例:

    sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp + 'GET' + '/users/self/verify', SecretKey))

  2. 时间戳值与 OK-ACCESS-TIMESTAMP 标头相同,精度为纳秒。

  3. 请求方法必须是大写,即GET和POST。

  4. requestPath是请求端点的路径。例子:

    /orders?before=2&limit=30

  5. body指的是请求体的String。如果没有请求体可以省略 (通常是 GET 请求的情况)。例子:

    {"product_id":"BTC-USD-0309","order_id":"377454671037440"}

  6. SecretKey是在你创建APIKey的时候生成的。例子:

    22582BD0CFF14C41EDBF1AB98506286D*

import hmac
import base64
import requests
import json

from Secrets import okex_key
from Secrets import okex_secret
from Secrets import okex_pass

#get time
def get_time():
    urltime= 'https://www.okex.com/api/general/v3/time'
    response=requests.get(urltime)
    time=response.json()
    time=time['iso']
    return time

# signature
def signature(timestamp, method, request_path, body,secret_key):
    if str(body) == '{}' or str(body) == 'None':
        body = ''
    message = str(timestamp) + str.upper(method) + request_path + str(body)
    mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
    d = mac.digest()
    return base64.b64encode(d)


# set request header
def get_header():
    body= {}
    request= 'GET'
    endpoint= '/api/spot/v3/accounts'
    header = dict()
    header['CONTENT-TYPE'] = 'application/json'
    header['OK-ACCESS-KEY'] = okex_key
    header['OK-ACCESS-SIGN'] = signature(get_time(), request, endpoint , body, okex_secret)
    header['OK-ACCESS-TIMESTAMP'] = str(get_time())
    header['OK-ACCESS-PASSPHRASE'] = okex_pass
    return header


url = 'http://www.okex.com/api/spot/v3/accounts'
header = get_header()
response= requests.get(url, headers=header)
response.json()
python api authentication python-requests sign
4个回答
3
投票

问题是在计算

OK-ACCESS-SIGN
OK-ACCESS-TIMESTAMP
的值时,您给出了两个不同的时间值。将
get_time()
放入单个变量并在两个地方使用它。

current_time = get_time()

header['OK-ACCESS-SIGN'] = signature(current_time, request, endpoint , body, okex_secret)
header['OK-ACCESS-TIMESTAMP'] = str(current_time)

还要注意,目前,在

get_time
,你询问OKEx的服务器现在几点了。但是,如果您的计算机的本地时间相当正确(例如,您可以在 https://time.is 上查看),您可以避免执行该 HTTP 请求,而只使用本地时间。

import datetime

def get_time():
    now = datetime.datetime.utcnow()
    t = now.isoformat("T", "milliseconds")
    return t + "Z"

3
投票

这是我对 OKEx API 签名请求的解决方案。 感谢 zoe-fleischerrok-povsic

import base64
import datetime as dt
import hmac
import requests

APIKEY = "xxxxx"
APISECRET = "xxxx"
PASS = "xxxx"
BASE_URL = 'https://aws.okex.com'

def send_signed_request(http_method, url_path, payload={}):
    '''
    See https://stackoverflow.com/questions/66486374/how-to-sign-an-okex-api-request
    '''
    def get_time():
        return dt.datetime.utcnow().isoformat()[:-3]+'Z'
    
    def signature(timestamp, method, request_path, body, secret_key):
        if str(body) == '{}' or str(body) == 'None':
            body = ''
        message = str(timestamp) + str.upper(method) + request_path + str(body)
        mac = hmac.new(bytes(secret_key, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
        d = mac.digest()
        return base64.b64encode(d)
    
    # set request header
    def get_header(request='GET', endpoint='', body:dict=dict()):
        cur_time = get_time()
        header = dict()
        header['CONTENT-TYPE'] = 'application/json'
        header['OK-ACCESS-KEY'] = APIKEY
        header['OK-ACCESS-SIGN'] = signature(cur_time, request, endpoint , body, APISECRET)
        header['OK-ACCESS-TIMESTAMP'] = str(cur_time)
        header['OK-ACCESS-PASSPHRASE'] = PASS
        return header

    url = BASE_URL + url_path
    header = get_header(http_method, url_path, payload)
    print(url)
    print(header)
    response = requests.get(url, headers=header)
    response.json()
    return response.json()

send_signed_request("GET", "/api/v5/account/balance", payload={})

0
投票

我在 Postman 中执行 REST API,它对我来说工作正常。以下是步骤。 (您需要使用 okex time api 的时间戳)

获取:

https://www.okex.com/api/v5/account/balance

标题:

OK-ACCESS-KEY:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (API_KEY)
OK-ACCESS-SIGN:{{sign}}
OK-ACCESS-TIMESTAMP:{{timestamp}}
OK-ACCESS-PASSPHRASE:YOUR_PASSPHRASE
Content-Type:application/json

预请求脚本

pm.sendRequest('https://www.okex.com/api/general/v3/time', function (err, res) {
        console.log('Response_ISO: '+res.json().iso);
        pm.expect(err).to.not.be.ok;
        var timestamp = res.json().iso;
        var sign = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp + 'GET' + '/api/v5/account/balance', 'YOUR_SECRET_KEY'));
        console.log(sign);

        postman.setEnvironmentVariable('timestamp',timestamp)  
        postman.setGlobalVariable('timestamp',timestamp) 

        postman.setEnvironmentVariable('sign',sign)  
        postman.setGlobalVariable('sign',sign)
    }); 

0
投票

如果有人对简单的发布方法感兴趣,这就是我在 dart (Flutter) 中使用的方式。我买了一个有止盈和止损的代币

import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart';

class ApiOKX {

  static Map<String, String> headers(String method, api, String body) {

    String time = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(DateTime.now().toUtc());
    List<int> messageBytes = utf8.encode('$time$method$api$body');
    List<int> key = utf8.encode('secret');
    Hmac hash = Hmac(sha256, key);
    Digest digest = hash.convert(messageBytes);
    String token = base64.encode(digest.bytes);

    var headers = {

      // For OKX
      'OK-ACCESS-KEY': 'accessKey',
      'OK-ACCESS-SIGN': accessSign,
      'OK-ACCESS-TIMESTAMP': time,
      'OK-ACCESS-PASSPHRASE': 'your password',
      'Content-Type': 'application/json',
      'x-simulated-trading': '1'
    };

    return headers;
  }

  static Future<String?> post(BuildContext context, api, {body, params})  async {

    var uri = Uri.parse('$base$api');

    print(uri);
    print('POST');
    var b = jsonEncode(body);
    log('body: $b');

    var response = await http.post(
        uri,
        headers: headers('POST', api, b),
        body: b
    );

    if (response.statusCode == 200) {
      return response.body;
    } else {
      print('Request failed with status: ${response.statusCode}. ${response.body}');
    }

    return null;
  }

随时随地打电话

  _buy() async {

    var payload =  {
      "instId": '${_leftCoin.text.toUpperCase()}-${_sideCoin.text.toUpperCase()}',
      "tdMode": "cash",
      "side": "buy",
      "ordType": "market",
      "sz": "100",
      "tpTriggerPx": _tp.text,
      "tpOrdPx": _tp.text,
      "slTriggerPx": _sl.text,
      "slOrdPx": _sl.text
    };

    var res = await ApiOKX.post(context, ApiOKX.order, body: payload);

    if (res != null) {

      var parsed = json.decode(res);

      if (parsed['data'].length > 0) {

        if (parsed['code'] == '0') {
          Toaster.success(context, parsed['data'][0]['sMsg']);
        }
        else {
          Toaster.error(context, parsed['data'][0]['sMsg']);
        }

      }
    }
  }
© www.soinside.com 2019 - 2024. All rights reserved.