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

Rust实现HMAC-SHA512签名无法得到Kraken API预期结果

问题原因

你的签名逻辑有3个核心错误,全部出在HMAC计算前的预处理环节,和HMAC库本身无关:

  • 多余的URL编码操作:传入的params已经是符合接口要求的x-www-form-urlencoded格式表单字符串,你对整个字符串做urlencode会把=、&这类合法表单字符转义为%3D、%26,完全改变了待哈希的内容
  • nonce重复拼接:测试用例传入的params已经包含了nonce=1616492376594字段,你额外把nonce的纯数字值拼在参数最前面,导致待哈希内容里nonce重复出现
  • SHA256结果格式错误:你使用的sha256::digest函数默认返回十六进制编码的字符串(长度64字节),但接口要求使用SHA256的原始32字节二进制摘要拼接URI,用十六进制字符串拼接会完全改变待HMAC内容,且二进制字节直接和UTF8字符串拼接还可能出现隐式编码错误
修正方案
  1. 移除对params的全局urlencode逻辑,表单参数按传入的原始格式使用即可
  2. 不要额外拼接独立nonce值,直接使用完整的POST表单字符串(包含nonce字段)作为SHA256输入
  3. 调用SHA256接口时获取原始二进制字节结果,不要用十六进制字符串
  4. 全程使用字节数组拼接待签名内容,不要强制转成UTF8字符串,避免非法二进制字节导致的内容错误
修正后可运行代码

现有Cargo.toml依赖不需要改动,sha256 1.0.3版本已提供digest_bytes方法返回原始字节数组:

[dependencies]
urlencoding = "2.1.0"
base64 = "0.13.0"
ring = "0.16.20"
sha256 = "1.0.3"

签名代码修正如下:

use ring::hmac;
use sha256::digest_bytes;

pub fn api_sign(
    private_key: Option<String>,
    nonce: u64, // 修正原拼写错误nonse -> nonce
    params: Option<String>,
    uri: String,
) -> hmac::Tag {
    let private_key = private_key.expect("Private key is not provided");
    
    // 处理POST参数:如果params没带nonce就补上,带了直接用,不要做全局urlencode
    let post_data = match params {
        Some(p) => {
            if p.starts_with("nonce=") {
                p
            } else {
                format!("nonce={}&{}", nonce, p)
            }
        },
        None => format!("nonce={}", nonce),
    };

    // 计算SHA256原始字节,不要转十六进制字符串
    let sha256_hash = digest_bytes(post_data.as_bytes());

    // 拼接URI字节 + SHA256原始字节,全程用u8数组不要转String
    let mut hmac_data = uri.as_bytes().to_vec();
    hmac_data.extend_from_slice(&sha256_hash);

    // 私钥base64解码后做HMAC-SHA512
    let key = base64::decode(private_key).unwrap();
    let key = hmac::Key::new(hmac::HMAC_SHA512, &key);

    let mut s_ctx = hmac::Context::with_key(&key);
    s_ctx.update(&hmac_data);
    s_ctx.sign()
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_api_sign_0() {
        assert_eq!(
            base64::encode(api_sign(
                Some("kQH5HW/8p1uGOVjbgWA7FunAmGO8lsSUXNsu3eow76sz84Q18fWxnyRzBHCd3pd5nE9qa99HAZtuZuj6F1huXg==".to_string()),
                1616492376594,
                Some("nonce=1616492376594&ordertype=limit&pair=XBTUSD&price=37500&type=buy&volume=1.25".to_string()),
                "/0/private/AddOrder".to_string()
            ).as_ref()),
            "4/dpxb3iT4tp/ZCVEwSnEsLxx0bqyhLpdfOpc6fn7OR8+UClSV5n9E6aSS8MPtnRfp32bAb0nmbRn6H8ndwLUQ=="
        )
    }
}
补充说明

如果后续需要对单个参数值做urlencode,只需要在拼接表单键值对的时候单独编码参数值即可,不要对拼接完成的整个表单字符串做编码,否则会破坏key=value&key2=value2的表单结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 11:48:25