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

Kraken API返回EAPI: Invalid Key/Signature错误排查求助

Kraken API 认证错误排查与修复

问题根源

你的代码存在两处关键错误,导致返回EAPI: Invalid Key或EAPI: Invalid Signature:

1. HTTP请求头键名格式错误

请求头的键名不应包含冒号和空格,你使用的"API-Key: "、"API-Sign: "不符合HTTP规范,服务器无法正确识别这些字段,因此返回密钥无效错误。正确的键名应为"API-Key"和"API-Sign"。

2. 签名计算逻辑不符合Kraken规范

Kraken签名生成的正确流程是:

  • 对nonce + postData的原始字节计算SHA256哈希(保留二进制结果,而非转为十六进制字符串)
  • 将API路径(如/0/private/Balance)与上述SHA256二进制结果拼接
  • 用Base64解码后的API密钥作为密钥,对拼接内容计算HMAC-SHA512
  • 最后将HMAC结果进行Base64编码得到签名

你的代码错误地将SHA256结果转为十六进制字符串后再参与HMAC计算,导致签名不匹配。

修复后的代码

Trade.h(无需修改)

#pragma warning(disable : 4996)
#include <iostream>
#include <iomanip>
#include <string>
#include <cpr/cpr.h>
#include <nlohmann/json.hpp>
#include <chrono>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

using namespace std;
using namespace cpr;
using namespace chrono;
using json = nlohmann::json;

class Trade
{
private:
    string key;
    string secret;
    string timestamp;
    string signature;
    string baseUrl;
    string postData;

public:
    Trade();
    void Authenticate();
    void getAccounts();
};

Trade.cpp(修改签名计算和请求头部分)

#include "Trade.h"

Trade::Trade() {
    key = "xxxxxxxxxxxxxxxx";
    secret = "xxxxxxxxxxxxxxxxxxx";
    postData = "";
    timestamp = "";
    signature = "";
    baseUrl = "https://api.kraken.com/0/private/Balance";
}

static std::string b64_decode(const std::string& data)
{
    BIO * b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO* bio = BIO_new_mem_buf(data.c_str(), data.size());
    bio = BIO_push(b64, bio);

    char buffer[512];
    int len;
    std::string decoded;
    while ((len = BIO_read(bio, buffer, sizeof(buffer))) > 0) {
        decoded.append(buffer, len);
    }

    BIO_free_all(bio);
    return decoded;
}

static std::string b64_encode(const std::string& data)
{
    BIO* b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    BIO* mem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, mem);

    BIO_write(b64, data.c_str(), data.length());
    BIO_flush(b64);

    BUF_MEM* mem_buf = NULL;
    BIO_get_mem_ptr(b64, &mem_buf);
    std::string output(mem_buf->data, mem_buf->length());

    BIO_free_all(b64);
    return output;
}

// 修改SHA256函数,返回二进制哈希结果
static string sha256_bin(const string& str) {
    EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
    const EVP_MD* md = EVP_sha256();
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int hash_len;

    EVP_DigestInit_ex(mdctx, md, NULL);
    EVP_DigestUpdate(mdctx, str.c_str(), str.size());
    EVP_DigestFinal_ex(mdctx, hash, &hash_len);

    EVP_MD_CTX_free(mdctx);

    // 返回二进制字符串,而非十六进制格式
    return string(reinterpret_cast<char*>(hash), hash_len);
}

static std::string hmac_sha512(const std::string& data, const std::string& key) {
    std::string result;
    HMAC_CTX* hmac_ctx = HMAC_CTX_new();
    HMAC_Init_ex(hmac_ctx, key.c_str(), key.length(), EVP_sha512(), NULL);

    HMAC_Update(hmac_ctx, (const unsigned char*)data.c_str(), data.length());

    unsigned char hmac_result[EVP_MAX_MD_SIZE];
    unsigned int hmac_len;
    HMAC_Final(hmac_ctx, hmac_result, &hmac_len);
    result.assign((const char*)hmac_result, hmac_len);

    HMAC_CTX_free(hmac_ctx);
    return result;
}

void Trade::Authenticate() {
    const auto p1 = system_clock::now();
    // 改用毫秒级时间戳作为nonce,降低重复风险
    unsigned long long intTimestamp = duration_cast<milliseconds>(p1.time_since_epoch()).count();
    timestamp = to_string(intTimestamp);

    string path = "/0/private/Balance";
    postData = "nonce=" + timestamp;

    // 计算nonce+postData的二进制SHA256哈希
    string nonce_post = timestamp + postData;
    string sha256_result = sha256_bin(nonce_post);

    // 拼接API路径与SHA256二进制结果
    string hmac_input = path + sha256_result;
    // 用Base64解码后的密钥计算HMAC,再编码为Base64得到签名
    signature = b64_encode(hmac_sha512(hmac_input, b64_decode(secret)));
}

void Trade::getAccounts() {
    Response r;
    
    Header header;
    // 修正请求头键名,去掉多余的冒号和空格
    header.insert({ "API-Key", key });
    header.insert({ "API-Sign", signature });
    header.insert({ "User-Agent", "Kraken C++ API Client" });
    header.insert({ "Content-Type", "application/x-www-form-urlencoded" });

    r = Post(Url{ baseUrl }, Body{postData}, Header{ header });
    cout << "Status Code: " << r.status_code << endl;
    cout << r.text << endl;
}

main.cpp(无需修改)

#include<iostream>
#include "Trade.h"

using namespace std;

int main() {
    Trade trade;
    trade.Authenticate();
    trade.getAccounts();
}

额外注意事项

  • 确保API密钥拥有Balance接口的访问权限
  • Nonce必须严格递增,使用毫秒级时间戳可有效避免重复
  • 每次请求必须重新计算签名,不能复用之前的签名值

内容的提问来源于stack exchange,提问作者John Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:25:05