Khi làm việc với các sàn giao dịch tiền mã hóa như OKX, việc implement authentication đúng cách là yếu tố then chốt để đảm bảo tính bảo mật cho các API requests. Bài viết này sẽ hướng dẫn chi tiết cách implement HMAC signature authentication cho OKX API từ cơ bản đến nâng cao.

Tổng quan về OKX API Authentication

OKX sử dụng phương thức HMAC-SHA256 để ký các API requests. Mỗi request cần có signature được tạo từ việc kết hợp các tham số request với secret key của bạn. Điều quan trọng cần hiểu là signature không phải là mã hóa - nó là cơ chế xác thực để chứng minh bạn sở hữu secret key mà không cần gửi key đó qua network.

Các thành phần cần thiết

Implementation chi tiết

Bước 1: Cài đặt dependencies

# Python
pip install requests crypto-js
npm install crypto-js axios  # Node.js

Bước 2: Implement HMAC Signature Function

# Python implementation
import hmac
import hashlib
import time
import requests
from urllib.parse import urlencode

class OKXAuthenticator:
    def __init__(self, api_key: str, secret_key: str, passphrase: str, use_sandbox: bool = False):
        self.api_key = api_key
        self.secret_key = secret_key
        self.passphrase = passphrase
        self.base_url = "https://www.okx.com" if not use_sandbox else "https://www.okx.com"
        
    def _sign(self, timestamp: str, method: str, path: str, body: str = "") -> str:
        """
        Tạo HMAC-SHA256 signature
        Signature = HMAC-SHA256(secretKey, timestamp + method + path + body)
        """
        message = timestamp + method + path + body
        signature = hmac.new(
            self.secret_key.encode('utf-8'),
            message.encode('utf-8'),
            hashlib.sha256
        ).digest()
        return signature.hex().upper()
    
    def _get_headers(self, method: str, path: str, body: str = "") -> dict:
        timestamp = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())
        signature = self._sign(timestamp, method, path, body)
        
        return {
            'OK-ACCESS-KEY': self.api_key,
            'OK-ACCESS-SIGN': signature,
            'OK-ACCESS-TIMESTAMP': timestamp,
            'OK-ACCESS-PASSPHRASE': self.passphrase,
            'Content-Type': 'application/json'
        }
    
    def request(self, method: str, endpoint: str, params: dict = None, body: dict = None):
        path = endpoint
        if params:
            path += '?' + urlencode(params)
        
        body_str = json.dumps(body) if body else ""
        headers = self._get_headers(method, endpoint, body_str)
        
        url = self.base_url + endpoint
        response = requests.request(method, url, headers=headers, params=params, json=body)
        return response.json()

Bước 3: Sử dụng Authentication

# Python - Ví dụ thực tế
import json

Khởi tạo authenticator

auth = OKXAuthenticator( api_key="YOUR_API_KEY", secret_key="YOUR_SECRET_KEY", passphrase="YOUR_PASSPHRASE" )

Lấy thông tin tài khoản

account_info = auth.request("GET", "/api/v5/account/balance") print(json.dumps(account_info, indent=2))

Đặt lệnh mua BTC

order_params = { "instId": "BTC-USDT", "tdMode": "cash", "side": "buy", "ordType": "limit", "sz": "0.01", "px": "50000" } order_result = auth.request("POST", "/api/v5/trade/order", body=order_params) print(json.dumps(order_result, indent=2))

Node.js Implementation

// Node.js implementation
const crypto = require('crypto-js');
const axios = require('axios');

class OKXClient {
    constructor(apiKey, secretKey, passphrase, passphraseIV, useSandbox = false) {
        this.apiKey = apiKey;
        this.secretKey = secretKey;
        this.passphrase = passphrase;
        this.passphraseIV = passphraseIV;
        this.baseURL = "https://www.okx.com";
    }
    
    // Mã hóa passphrase bằng AES
    _encryptPassphrase() {
        return crypto.AES.encrypt(this.passphrase, this.passphraseIV).toString();
    }
    
    // Tạo signature
    _sign(timestamp, method, requestPath, body) {
        const message = timestamp + method + requestPath + body;
        return crypto.HmacSHA256(message, this.secretKey).toString(crypto.enc.Hex).toUpperCase();
    }
    
    // Tạo headers cho request
    _getHeaders(method, requestPath, body = '') {
        const timestamp = new Date().toISOString();
        const bodyString = body ? JSON.stringify(body) : '';
        const signature = this._sign(timestamp, method, requestPath, bodyString);
        const encryptedPassphrase = this._encryptPassphrase();
        
        return {
            'OK-ACCESS-KEY': this.apiKey,
            'OK-ACCESS-SIGN': signature,
            'OK-ACCESS-TIMESTAMP': timestamp,
            'OK-ACCESS-PASSPHRASE': encryptedPassphrase,
            'Content-Type': 'application/json'
        };
    }
    
    async request(method, endpoint, params = null, body = null) {
        const bodyString = body ? JSON.stringify(body) : '';
        const headers = this._getHeaders(method, endpoint, bodyString);
        
        let url = this.baseURL + endpoint;
        if (params) {
            url += '?' + new URLSearchParams(params).toString();
        }
        
        try {
            const response = await axios({
                method,
                url,
                headers,
                params,
                data: body
            });
            return response.data;
        } catch (error) {
            console.error('API Error:', error.response?.data || error.message);
            throw error;
        }
    }
    
    // Lấy danh sách vị thế
    async getPositions() {
        return this.request('GET', '/api/v5/account/positions');
    }
    
    // Lấy thông tin tài khoản
    async getAccountBalance() {
        return this.request('GET', '/api/v5/account/balance');
    }
    
    // Đặt lệnh
    async placeOrder(instId, side, ordType, sz, px = null) {
        const orderData = {
            instId,
            tdMode: 'cash',
            side,
            ordType,
            sz
        };
        if (px) orderData.px = px;
        
        return this.request('POST', '/api/v5/trade/order', null, orderData);
    }
}

// Sử dụng
const client = new OKXClient(
    'YOUR_API_KEY',
    'YOUR_SECRET_KEY',
    'YOUR_PASSPHRASE',
    'YOUR_PASSPHRASE_IV'
);

(async () => {
    try {
        const balance = await client.getAccountBalance();
        console.log('Balance:', JSON.stringify(balance, null, 2));
    } catch (error) {
        console.error('Error:', error);
    }
})();

Các loại signature requests

OKX API yêu cầu signature cho các endpoint khác nhau với các tham số khác nhau:

Lỗi thường gặp và cách khắc phục

1. Lỗi "signature verification failed"

Nguyên nhân: Signature không khớp với server expectations

# Cách khắc phục - Debug signature
def debug_signature(timestamp, method, path, body, secret_key):
    message = timestamp + method + path + body
    print(f"Message: {repr(message)}")
    print(f"Message length: {len(message)}")
    
    signature = hmac.new(
        secret_key.encode('utf-8'),
        message.encode('utf-8'),
        hashlib.sha256
    ).digest().hex().upper()
    print(f"Signature: {signature}")
    return signature

Kiểm tra xem timestamp format có đúng không

OKX yêu cầu: 2024-01-15T10:30:00.000Z

2. Lỗi "invalid passphrase"

Nguyên nhân: Passphrase không được mã hóa đúng cách hoặc sai passphrase

# Python - Mã hóa passphrase nếu cần
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import base64

def encrypt_passphrase(passphrase, passphraseIV, secretKey):
    """
    Mã hóa passphrase sử dụng AES-256-CBC
    passphraseIV và secretKey được cung cấp khi tạo API key
    """
    cipher = Cipher(
        algorithms.AES(secretKey.encode('utf-8')),
        modes.CBC(passphraseIV.encode('utf-8')),
        backend=default_backend()
    )
    encryptor = cipher.encryptor()
    
    # PKCS7 padding
    block_size = 16
    padding_length = block_size - (len(passphrase) % block_size)
    padded_data = passphrase + chr(padding_length) * padding_length
    
    encrypted = encryptor.update(padded_data.encode('utf-8')) + encryptor.finalize()
    return base64.b64encode(encrypted).decode('utf-8')

3. Lỗi timestamp out of range

Nguyên nhân: Timestamp của request chênh lệch quá nhiều so với server time

# Python - Đồng bộ thời gian với server
import time
import requests

def get_server_time():
    """Lấy thời gian server OKX"""
    response = requests.get("https://www.okx.com/api/v5/public/time")
    data = response.json()
    if data['code'] == '0':
        return int(data['data'][0]['ts']) / 1000  # Convert ms to seconds
    return None

def sync_timestamp(authenticator):
    """Đồng bộ timestamp với server OKX"""
    server_time = get_server_time()
    if server_time:
        local_time = time.time()
        drift = server_time - local_time
        print(f"Time drift: {drift:.2f} seconds")
        return drift
    return 0

Sử dụng - gọi khi khởi tạo authenticator

time_drift = sync_timestamp(authenticator)

4. Lỗi "insufficient permissions"

Nguyên nhân: API key không có quyền truy cập endpoint đó

# Kiểm tra và xử lý permission errors
def handle_api_error(response):
    error_codes = {
        '58001': 'Insufficient trading permissions',
        '58002': 'Insufficient reading permissions',
        '58003': 'Insufficient transfer permissions',
        '58004': 'Insufficient withdrawal permissions',
        '58005': 'Insufficient account permissions'
    }
    
    if response.get('code') != '0':
        code = response.get('code')
        msg = response.get('msg', 'Unknown error')
        
        if code in error_codes:
            print(f"Permission Error: {error_codes[code]}")
            print(f"Details: {msg}")
            print("Solution: Please regenerate API key with correct permissions")
        else:
            print(f"API Error {code}: {msg}")
        return True
    return False

Best Practices bảo mật

Kết luận

Việc implement OKX API authentication đòi hỏi sự chính xác trong từng bước. Hãy đảm bảo timestamp format chính xác, signature được tạo đúng theo specification, và passphrase được xử lý phù hợp với cấu hình API key của bạn. Nếu gặp khó khăn, hãy bắt đầu với sandbox environment trước khi chuyển sang production.