Gate io api V4 INVALID_SIGNATURE

138 views Asked by At

I am trying to do a request to Gate.io cryptocurrency market and getting this error. {"label":"INVALID_SIGNATURE","message":"Signature mismatch"}

C++ code:

std::string hmac_sha512(const std::string& key, const std::string& data) {
    unsigned char* digest = HMAC(EVP_sha512(), key.c_str(), key.length(),
        reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), nullptr, nullptr);
    std::stringstream ss;
    for (int i = 0; i < SHA512_DIGEST_LENGTH; i++) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)digest[i];
    }
    return ss.str();
}
CreateOrder(){
std::string host = "api.gateio.ws";
std::string prefix = "/api/v4";
std::string path = "/spot/orders";
std::string method = "POST";


//https://api.gateio.ws/api/v4/spot/orders
std::string payload = "{\"text\":\"t-123456\",\"currency_pair\":\"ETH_BTC\",\"type\":\"limit\",\"account\":\"spot\",\"side\":\"buy\",\"iceberg\":\"0\",\"amount\":\"1\",\"price\":\"5.00032\",\"time_in_force\":\"gtc\",\"auto_borrow\":false,\"stp_act\":\"cn\"}";
std::string hashJsonPayload = hmac_sha512(secretKey, payload);
std::string timestamp_seconds= std::to_string(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count());
std::string queryParam = "";
std::string sign_string = method + "\n" + "/api/v4/spot/orders"  + "\n" + queryParam + "\n" + hashJsonPayload + "\n" + timestamp_seconds;

std::string signHash = hmac_sha512(secretKey, sign_string);

req_.method(boost::beast::http::verb::post);
req_.target(r.target);
req_.version(11);
req_.set(boost::beast::http::field::host, r.host);
req_.set(boost::beast::http::field::accept, "application/json");
req_.set(boost::beast::http::field::content_type, "application/json");
req_.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req_.set("KEY", api_key); // API anahtarı ekleniyor
req_.set("Timestamp", timestamp_seconds);
req_.set("SIGN", signHash);
..
}
    

I am trying change payload but not working std::string payload1 = "{"text":"t-123456","currency_pair":"ETH_BTC","type":"limit","account":"spot","side":"buy","iceberg":"0","amount":"1","price":"5.00032","time_in_force":"gtc","auto_borrow":false,"stp_act":"cn"}";

std::string payload2 = R"(
{
    "text": "t-123456",
    "currency_pair": "ETH_BTC",
    "type": "limit",
    "account": "spot",
    "side": "buy",
    "iceberg": "0",
    "amount": "1",
    "price": "5.00032",
    "time_in_force": "gtc",
    "auto_borrow": false,
    "stp_act": "cn"
}
)";

std::string payload3= "{"account":"spot","currency_pair":"" + currencyPair + "","type":"market","side":"" + side + "","amount":"" + amount + ""}";

2

There are 2 answers

1
sehe On BEST ANSWER

The documentation here:

HexEncode(SHA512(Request Payload))

Hash the request body with SHA512 and output its Hex encoded form. If no request body, use empty string's hashed result, i.e. cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e

Note that you need HexEncode(SHA512(Request Payload)), you used hmac_sha512 which is a key-based encryption based on SHA512, but not the same. This is easily proven by the fact that the hash for the empty payload is given.

DEMO

Attempted fix:

Live On Coliru

#include <chrono>
#include <iostream>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <span>

using namespace std::chrono_literals;
static constexpr auto now = std::chrono::system_clock::now;

using Bytes = std::span<unsigned char>;

static std::string hex_digest(Bytes hash) {
    std::string result(hash.size() * 2, '0');

    for (auto out = result.begin(); int b : hash) {
        static constexpr char digits[] = "0123456789abcdef";
        *out++ = digits[b >> 4];
        *out++ = digits[b & 0xF];
    }
    return result;
}

static std::string sha512(std::string const& data) {
    std::array<unsigned char, SHA512_DIGEST_LENGTH> md;
    unsigned                                        n = md.size();
    if (!EVP_Digest(reinterpret_cast<unsigned char const*>(data.data()), data.length(), //
                    md.data(), &n, EVP_sha512(), nullptr))
        throw std::runtime_error(__FUNCTION__);
    return hex_digest(md);
}
static std::string hmac_sha512(std::string const& key, std::string const& data) {
    unsigned char* digest =
        HMAC(EVP_sha512(), key.c_str(), key.length(), reinterpret_cast<unsigned char const*>(data.c_str()),
             data.length(), nullptr, nullptr);

    return hex_digest(Bytes(digest, SHA512_DIGEST_LENGTH));
}

int main() {
    struct {
        std::string target = "/", host = "host";
    } r;

    std::string host = "api.gateio.ws", prefix = "/api/v4", path = "/spot/orders", method = "POST";
    r = {prefix + path, host};
    std::string payload =
        "{\"text\":\"t-123456\",\"currency_pair\":\"ETH_BTC\",\"type\":\"limit\",\"account\":\"spot\","
        "\"side\":\"buy\",\"iceberg\":\"0\",\"amount\":\"1\",\"price\":\"5.00032\",\"time_in_force\":"
        "\"gtc\",\"auto_borrow\":false,\"stp_act\":\"cn\"}";
    std::string timestamp_seconds = std::to_string(now().time_since_epoch() / 1s);
    std::string queryParam        = "";
    std::string sign_string =
        method + "\n" + r.target + "\n" + queryParam + "\n" + sha512(payload) + "\n" + timestamp_seconds;
    std::cout << sign_string << "\n\n" << hmac_sha512("secret", sign_string) << "\n";
}

Demo:

enter image description here

1
akdrkr On

Thank you very much, the point I missed was encrypting the payload with hmac_sha512, when I encrypted it with sha512, the signature error was resolved.

std::string sha512(const std::string& input) {
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512_CTX shaContext;
SHA512_Init(&shaContext);
SHA512_Update(&shaContext, input.c_str(), input.length());
SHA512_Final(hash, &shaContext);

std::stringstream ss;
for (int i = 0; i < SHA512_DIGEST_LENGTH; ++i) {
    ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
}
return ss.str(); }


std::string hmac_sha512(const std::string& key, const std::string& data) {
unsigned char* digest = HMAC(EVP_sha512(), key.c_str(), key.length(),
    reinterpret_cast<const unsigned char*>(data.c_str()), data.length(), nullptr, nullptr);
std::stringstream ss;
for (int i = 0; i < SHA512_DIGEST_LENGTH; i++) {
    ss << std::hex << std::setw(2) << std::setfill('0') << (int)digest[i];
}

I use two functions this way.