跳到主要内容

身份验证

HashNut API v3.0.0 对所有 API 请求使用基于 HMAC-SHA256 签名的身份验证。这确保了安全、防篡改的 API 通信。

所需凭证

在发出 API 请求之前,您需要:

  • accessKeyId: 您的商户访问密钥 ID (公共标识符)
  • apiKey: 您的 API 密钥 (用于请求签名的私密密钥)
  • responseKey: 您的响应密钥 (用于 webhook 签名验证,可以与 apiKey 相同)

请求签名生成

所有经过身份验证的 API 请求必须包含以下标头:

标头描述示例
hashnut-request-uuid请求的唯一 UUID550e8400-e29b-41d4-a716-446655440000
hashnut-request-timestamp时间戳 (nonce),以毫秒为单位1704067200000
hashnut-request-signHMAC-SHA256 签名 (Base64 编码)<base64-encoded-signature>
Content-Type请求内容类型application/json

签名算法

逐步过程

算法步骤:

  1. 生成 UUID: 创建唯一 UUID (例如,550e8400-e29b-41d4-a716-446655440000)
  2. 获取时间戳: 当前时间戳,以毫秒为单位 (例如,1704067200000)
  3. 字符串化请求体: 将请求体转换为 JSON 字符串 (无空格,精确格式)
  4. 连接: 组合 uuid + timestamp + body (无分隔符)
  5. 计算 HMAC: 使用 apiKey 作为密钥进行 HMAC-SHA256
  6. Base64 编码: 将 HMAC 结果编码为 Base64
  7. 设置标头: 使用 Base64 字符串作为 hashnut-request-sign 标头值

代码示例

import crypto from 'crypto';

function generateSignature(apiKey: string, body: object): {
uuid: string;
timestamp: string;
signature: string;
} {
// Step 1: Generate UUID
const uuid = crypto.randomUUID();

// Step 2: Get timestamp
const timestamp = Date.now().toString();

// Step 3: Stringify body (no whitespace)
const bodyString = JSON.stringify(body);

// Step 4: Concatenate
const signBody = uuid + timestamp + bodyString;

// Step 5 & 6: HMAC-SHA256 + Base64
const signature = crypto
.createHmac('sha256', apiKey)
.update(signBody)
.digest('base64');

return {
uuid,
timestamp,
signature
};
}

// Usage
const { uuid, timestamp, signature } = generateSignature(
'your-api-key',
{
accessKeyId: 'your-access-key-id',
merchantOrderId: 'order-123',
chainCode: 'erc20',
coinCode: 'usdt',
amount: 0.01
}
);

// Set headers
const headers = {
'hashnut-request-uuid': uuid,
'hashnut-request-timestamp': timestamp,
'hashnut-request-sign': signature,
'Content-Type': 'application/json'
};

重要注意事项

请求体字符串化

⚠️ 关键: 请求体必须完全按照发送时的格式进行字符串化:

  • 无额外空格: 使用 JSON.stringify(obj) 或等效方法,不进行格式化
  • 一致的顺序: 使用相同的键顺序 (或使用排序的键)
  • 精确格式: 匹配将在请求中发送的确切 JSON 格式

时间戳要求

  • 格式: Unix 纪元以来的毫秒数 (例如,1704067200000)
  • 新鲜度: 时间戳应该是最近的 (5 分钟内)
  • 唯一性: 与 UUID 结合以防止重放攻击

UUID 要求

  • 格式: 标准 UUID v4 格式
  • 唯一性: 每个请求必须具有唯一的 UUID
  • 生成: 使用加密安全的随机 UUID 生成

完整请求示例

curl -X POST 'https://testnet.hashnut.io/api/v3.0.0/pay/createPayOrderOnSplitWalletWithApiKey' \
-H 'hashnut-request-uuid: 550e8400-e29b-41d4-a716-446655440000' \
-H 'hashnut-request-timestamp: 1704067200000' \
-H 'hashnut-request-sign: <base64-encoded-hmac-sha256>' \
-H 'Content-Type: application/json' \
-d '{
"accessKeyId": "YOUR_ACCESS_KEY_ID",
"merchantOrderId": "order-123",
"chainCode": "erc20",
"coinCode": "usdt",
"amount": 0.01
}'

错误响应

无效签名

{
"code": -2,
"msg": "Invalid signature or credentials",
"data": null
}

常见原因:

  • 用于签名的 apiKey 不正确
  • 请求体字符串化不正确 (空格、键顺序)
  • 缺少或错误的标头
  • 时间戳格式不正确

缺少标头

{
"code": -2,
"msg": "Missing required headers",
"data": null
}

必需的标头:

  • hashnut-request-uuid
  • hashnut-request-timestamp
  • hashnut-request-sign
  • Content-Type: application/json

安全最佳实践

  1. 安全密钥存储:

    • 永远不要将 API 密钥提交到版本控制
    • 使用环境变量或密钥管理
    • 定期轮换密钥
  2. 签名验证:

    • 始终验证签名是否匹配预期格式
    • 使用恒定时间比较进行签名验证
    • 记录身份验证失败以进行监控
  3. 请求安全:

    • 对所有 API 请求使用 HTTPS
    • 实现请求超时处理
    • 验证所有请求参数
  4. 密钥管理:

    • 为测试网和生产环境使用不同的密钥
    • 实现密钥轮换策略
    • 监控密钥使用以发现异常

测试身份验证

测试签名生成

// Test with known values
const testBody = { accessKeyId: "test", amount: 1.0 };
const { uuid, timestamp, signature } = generateSignature("test-key", testBody);

console.log("UUID:", uuid);
console.log("Timestamp:", timestamp);
console.log("Signature:", signature);

本地验证签名

// Verify your signature generation
function verifySignature(apiKey, uuid, timestamp, body, expectedSignature) {
const signBody = uuid + timestamp + JSON.stringify(body);
const computed = crypto
.createHmac('sha256', apiKey)
.update(signBody)
.digest('base64');

return computed === expectedSignature;
}

下一步


准备进行 API 调用? 查看 订单端点 →