使用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 + "×tamp=" + 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解决方案
核心排查方向
- 私钥格式错误:libsodium的ed25519私钥是64字节(包含内嵌公钥),如果你的私钥是32字节的纯种子,需要用
crypto_sign_ed25519_seed_keypair生成完整密钥对,而非crypto_sign_ed25519_sk_to_pk。 - 签名消息一致性:确保签名的字符串和Python示例完全一致,包括参数顺序、UTF-8编码、无多余空格/转义字符。
- 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 + "×tamp=" + 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
相关产品推荐
相关产品推荐

