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

使用ed25519密钥调用Binance WebSocket API时签名无效问题

问题:Binance WebSocket API ed25519签名无效(错误码-1022)

向Binance WebSocket API地址 wss://ws-api.binance.com:443/ws-api/v3 发送下单请求时,使用ed25519密钥对订单数据签名,收到如下错误响应:

{"id":"my_new_order","status":400,"error":{"code":-1022,"msg":"Signature for this request is not valid."},"rateLimits":[{"rateLimitType":"REQUEST_WEIGHT","interval":"MINUTE","intervalNum":1,"limit":6000,"count":3}]}

已尝试将JSON参数按字母排序,但问题仍未解决。官方文档提供的Python示例代码可正常运行,当前使用libsodium库生成签名,C++代码如下:

std::string privateKeyData = "MY_PRIVATE_KEY";
if (privateKeyData.empty()) {
    return 1;
}

// Set up the request parameters
std::string symbol = "BTCUSDT";
std::string side = "BUY";
std::string type = "LIMIT";
std::string timeInForce = "IOC";
std::string quantity = "0.001";
std::string price = "50000";

// Timestamp the request
std::string timestampStr = timestamp();

// Construct the message to sign
std::string message = "apiKey=" + API_KEY + "&symbol=" + symbol +
    "&side=" + side + "&type=" + type + "&timeInForce=" + timeInForce +
    "&quantity=" + quantity + "&price=" + price + "&timestamp=" + timestampStr;

// Load the private key for signing
unsigned char privateKey[crypto_sign_ed25519_SECRETKEYBYTES];
if (crypto_sign_ed25519_sk_to_pk(privateKey, reinterpret_cast<const unsigned char*>(privateKeyData.data())) != 0) {
    std::cerr << "Error: Failed to load private key." << std::endl;
    return 1;
}

// Sign the message
unsigned char signature[crypto_sign_BYTES];
unsigned long long signatureLength;
if (crypto_sign_detached(signature, &signatureLength,
                         reinterpret_cast<const unsigned char*>(message.data()), message.size(),
                         privateKey) != 0) {
    std::cerr << "Error: Failed to sign message." << std::endl;
    return 1;
}

// Encode the signature as base64
char encodedSignature[crypto_sign_BYTES * 2 + 1];
if (sodium_bin2base64(encodedSignature, sizeof(encodedSignature), signature, signatureLength, sodium_base64_VARIANT_ORIGINAL) == nullptr) {
    std::cerr << "Error: Failed to encode signature." << std::endl;
    return 1;
}

// Add the signature to the request parameters
std::cout << "Signature: " << encodedSignature << std::endl;


std::string base64Signature(encodedSignature);

// Construct the JSON string manually
std::string jsonRequest = "{"
    "\"id\": \"my_new_order\",";
    "\"method\": \"order.place\",";
    "\"params\": {"
    "\"apiKey\": \"" + API_KEY + "\",";
    "\"price\": \"" + price + "\",";
    "\"quantity\": \"" + quantity + "\",";
    "\"side\": \"" + side + "\",";
    "\"symbol\": \"" + symbol + "\",";
    "\"timeInForce\": \"" + timeInForce + "\",";
    "\"timestamp\": " + timestampStr + ","
    "\"type\": \"" + type + "\",";
    "\"signature\": \"" + base64Signature + "\""
    "}"
    "}";

// Print the JSON string
std::cout << "JSON Request:\n" << jsonRequest << std::endl;

生成的JSON请求:

{"id": "my_new_order","method": "order.place","params": {"apiKey": "MY_API_KEY","price": "50000","quantity": "0.001","side": "BUY","symbol": "BTCUSDT","timeInForce": "IOC","timestamp": 1713859232000,"type": "LIMIT","signature": "GENERATED_SIGNATURE"}}

对比可行的Python请求payload:

{'id': 'my_new_order', 'method': 'order.place', 'params': {'apiKey': 'MY_API_KEY','price': '50000', 'quantity': '0.001', 'symbol': 'BTCUSDT', 'side': 'BUY', 'timeInForce': 'IOC', 'type': 'LIMIT', 'timestamp': 1713859985702, 'signature': 'GENERATED_SIGNATURE'}}

问题排查点与非libsodium解决方案

核心排查方向

  1. 私钥格式错误:libsodium的ed25519私钥是64字节(包含内嵌公钥),如果你的私钥是32字节的纯种子,需要用crypto_sign_ed25519_seed_keypair生成完整密钥对,而非crypto_sign_ed25519_sk_to_pk。
  2. 签名消息一致性:确保签名的字符串和Python示例完全一致,包括参数顺序、UTF-8编码、无多余空格/转义字符。
  3. Base64编码匹配:Binance要求标准Base64(RFC 4648),确认sodium_bin2base64的VARIANT_ORIGINAL未使用URL安全变体。

非libsodium解决方案:OpenSSL实现ED25519签名

以下是使用OpenSSL库实现签名的C++代码示例:

#include <openssl/evp.h>
#include <openssl/base64.h>
#include <string>
#include <iostream>
#include <cstring>

// Base64编码(无换行符)
std::string base64_encode(const unsigned char* data, size_t len) {
    BIO *bio = BIO_new(BIO_s_mem());
    BIO *b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    
    BIO_write(bio, data, len);
    BIO_flush(bio);
    
    BUF_MEM* buf;
    BIO_get_mem_ptr(bio, &buf);
    std::string result(buf->data, buf->length);
    
    BIO_free_all(bio);
    BUF_MEM_free(buf);
    return result;
}

// 加载32字节ED25519私钥种子(Base64解码后)
EVP_PKEY* load_ed25519_key(const std::string& base64_seed) {
    size_t decoded_len = base64_seed.size() * 3 / 4;
    unsigned char* seed = new unsigned char[decoded_len];
    EVP_DecodeBlock(seed, reinterpret_cast<const unsigned char*>(base64_seed.c_str()), base64_seed.size());
    decoded_len = EVP_DecodeLength(base64_seed.size());
    
    EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, seed, decoded_len);
    delete[] seed;
    return pkey;
}

// ED25519签名
std::string ed25519_sign(const std::string& message, EVP_PKEY* pkey) {
    EVP_MD_CTX* ctx = EVP_MD_CTX_new();
    EVP_SignInit(ctx, nullptr); // ED25519无需哈希算法
    EVP_SignUpdate(ctx, message.data(), message.size());
    
    size_t sig_len = EVP_PKEY_size(pkey);
    unsigned char* sig = new unsigned char[sig_len];
    EVP_SignFinal(ctx, sig, &sig_len, pkey);
    
    std::string base64_sig = base64_encode(sig, sig_len);
    delete[] sig;
    EVP_MD_CTX_free(ctx);
    return base64_sig;
}

// 示例timestamp函数(返回毫秒级字符串)
std::string timestamp() {
    return std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
}

int main() {
    std::string private_key_base64 = "YOUR_BASE64_ENCODED_32BYTE_SEED";
    std::string API_KEY = "YOUR_API_KEY";
    
    EVP_PKEY* pkey = load_ed25519_key(private_key_base64);
    if (!pkey) {
        std::cerr << "Failed to load private key" << std::endl;
        return 1;
    }
    
    // 请求参数
    std::string symbol = "BTCUSDT";
    std::string side = "BUY";
    std::string type = "LIMIT";
    std::string timeInForce = "IOC";
    std::string quantity = "0.001";
    std::string price = "50000";
    std::string timestampStr = timestamp();
    
    // 构造签名消息
    std::string message = "apiKey=" + API_KEY + "&symbol=" + symbol +
        "&side=" + side + "&type=" + type + "&timeInForce=" + timeInForce +
        "&quantity=" + quantity + "&price=" + price + "&timestamp=" + timestampStr;
    
    // 生成签名
    std::string signature = ed25519_sign(message, pkey);
    
    // 用nlohmann/json库构造JSON(避免手动拼接错误)
    // 以下为示例,需先引入nlohmann/json头文件
    /*
    nlohmann::json request = {
        {"id", "my_new_order"},
        {"method", "order.place"},
        {"params", {
            {"apiKey", API_KEY},
            {"price", price},
            {"quantity", quantity},
            {"side", side},
            {"symbol", symbol},
            {"timeInForce", timeInForce},
            {"timestamp", std::stoull(timestampStr)},
            {"type", type},
            {"signature", signature}
        }}
    };
    std::cout << request.dump() << std::endl;
    */
    
    // 手动拼接JSON(仅示例,推荐用JSON库)
    std::string jsonRequest = R"({"id": "my_new_order","method": "order.place","params": {"apiKey": ")" + API_KEY + R"(","price": ")" + price + R"(","quantity": ")" + quantity + R"(","side": ")" + side + R"(","symbol": ")" + symbol + R"(","timeInForce": ")" + timeInForce + R"(","timestamp": )" + timestampStr + R"(,"type": ")" + type + R"(","signature": ")" + signature + R"("}})";
    
    std::cout << "JSON Request:\n" << jsonRequest << std::endl;
    
    EVP_PKEY_free(pkey);
    return 0;
}

额外建议

  • 禁止手动拼接JSON,使用nlohmann/json等成熟库,避免转义字符、参数顺序错误导致签名不匹配。
  • 确保timestamp为毫秒级精度,且签名消息中的timestamp与JSON参数中的值完全一致。

内容的提问来源于stack exchange,提问作者Soumalya Sahoo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 13:19:56