> ## Documentation Index
> Fetch the complete documentation index at: https://docs.satogate.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 请求签名方法

> 了解 Satogate API 的 HMAC-SHA1 签名认证机制

## 1. 申请安全凭证

在第一次使用 API 之前，请申请安全凭证。安全凭证包括 ApiKey 和 ApiSecret：

* **ApiKey** 用于标识 API 调用者身份
* **ApiSecret** 用于加密签名字符串和服务器端验证签名字符串的密钥

<Warning>
  用户必须严格保管安全凭证，避免泄露。每个账号最多可以拥有一对 ApiKey / ApiSecret。
</Warning>

## 2. 生成签名串

有了安全凭证 ApiKey 和 ApiSecret 后，就可以生成签名串了。

假设用户的 ApiKey 和 ApiSecret 分别是：

* ApiKey: `11519269-5e35-****-****-09e355e00f77`
* ApiSecret: `0CDE6743F18F3DA********49FD47C1576671FD5`

<Note>这里只是示例，请根据用户实际申请的 ApiKey 和 ApiSecret 进行后续操作。</Note>

**请求头（Request Header）**：

| 参数名称         | 说明             | 示例值                                    |
| ------------ | -------------- | -------------------------------------- |
| FP-API-KEY   | 商户 API KEY     | `11519269-5e35-****-****-09e355e00f77` |
| FP-SIGN      | 商户请求签名串        | `zmmjn35mikh6pM3V7sUEuX4wyYM=`         |
| FP-TIMESTAMP | 请求发起或签名的时间戳（秒） | `1681973331`                           |

设**请求参数**如下：

| 参数名称          | 说明        | 参数值               |
| ------------- | --------- | ----------------- |
| Action        | 方法名       | DescribeInstances |
| Nonce         | 随机正整数     | 11886             |
| Region        | 实例所在区域    | ap-guangzhou      |
| InstanceIds.0 | 待查询的实例 ID | ins-09dx96dg      |
| Offset        | 偏移量       | 0                 |
| Limit         | 最大允许输出    | 20                |
| Version       | 接口版本号     | 2017-03-12        |

<Steps>
  <Step title="对参数排序">
    对所有请求参数按参数名的字典序（ASCII 码）升序排序。只按参数名进行排序，参数值保持对应即可。按 ASCII 码比大小，如 `InstanceIds.2` 要排在 `InstanceIds.12` 后面。

    ```json theme={null}
    {
      "Action": "DescribeInstances",
      "InstanceIds.0": "ins-09dx96dg",
      "Limit": 20,
      "Nonce": 11886,
      "Offset": 0,
      "Region": "ap-guangzhou",
      "Version": "2017-03-12"
    }
    ```
  </Step>

  <Step title="拼接请求字符串">
    将排序好的请求参数格式化成 `参数名称=参数值` 的形式，然后用 `&` 拼接在一起。

    <Note>参数值为原始值而非 URL 编码后的值。</Note>

    ```
    Action=DescribeInstances&InstanceIds.0=ins-09dx96dg&Limit=20&Nonce=11886&Offset=0&Region=ap-guangzhou&Version=2017-03-12
    ```
  </Step>

  <Step title="拼接签名原文字符串">
    签名原文串的拼接规则为：`请求方法 + 请求主机 + 请求路径 + ? + 请求字符串 + 时间戳`

    ```
    GETapi.satogate.io/getAddress?Action=DescribeInstances&InstanceIds.0=ins-09dx96dg&Limit=20&Nonce=11886&Offset=0&Region=ap-guangzhou&SecretId=AKIDz8krbsJ5yKBZQpn74WFkmLPx3*******&Timestamp=1465185768&Version=2017-03-121681973331
    ```
  </Step>

  <Step title="生成签名串">
    使用 HMAC-SHA1 算法对签名原文字符串进行签名，然后将生成的签名串使用 Base64 进行编码。

    <CodeGroup>
      ```php PHP theme={null}
      $apiSecret = '0CDE6743F18F3DA********49FD47C1576671FD5';
      $srcStr = 'GETcvm.tencentcloudapi.com/?Action=DescribeInstances&InstanceIds.0=ins-09dx96dg&Limit=20&Nonce=11886&Offset=0&Region=ap-guangzhou&SecretId=AKIDz8krbsJ5yKBZQpn74WFkmLPx3*******&Timestamp=1465185768&Version=2017-03-121681973331';
      $macHex = hash_hmac('sha1', $srcStr, $apiSecret, false);
      $signStr = base64_encode($macHex);
      echo $signStr;
      ```

      ```python Python theme={null}
      import hmac
      import hashlib
      import base64

      api_secret = '0CDE6743F18F3DA********49FD47C1576671FD5'
      src_str = 'GETapi.satogate.io/getAddress?Action=DescribeInstances&...'
      mac_hex = hmac.new(
          api_secret.encode('utf-8'),
          src_str.encode('utf-8'),
          hashlib.sha1
      ).hexdigest()
      sign_str = base64.b64encode(mac_hex.encode('utf-8')).decode('utf-8')
      print(sign_str)
      ```

      ```java Java theme={null}
      import javax.crypto.Mac;
      import javax.crypto.spec.SecretKeySpec;
      import java.util.Base64;

      String apiSecret = "0CDE6743F18F3DA********49FD47C1576671FD5";
      String srcStr = "GETapi.satogate.io/getAddress?Action=DescribeInstances&...";
      Mac mac = Mac.getInstance("HmacSHA1");
      mac.init(new SecretKeySpec(apiSecret.getBytes("UTF-8"), "HmacSHA1"));
      byte[] rawHmac = mac.doFinal(srcStr.getBytes("UTF-8"));
      StringBuilder hexStr = new StringBuilder();
      for (byte b : rawHmac) {
          hexStr.append(String.format("%02x", b));
      }
      String signStr = Base64.getEncoder().encodeToString(hexStr.toString().getBytes("UTF-8"));
      ```
    </CodeGroup>

    最终得到的签名串为：

    ```
    zmmjn35mikh6pM3V7sUEuX4wyYM=
    ```
  </Step>
</Steps>

## 3. 发起请求

生成的签名串放置在请求头（Request Header）中，参数名为 `FP-SIGN`。

## 4. 签名失败

根据实际情况，存在以下签名失败的错误码，请根据实际情况处理。

| 错误代码  | 错误信息              | 错误描述        |
| ----- | ----------------- | ----------- |
| 10027 | Signature expire  | 签名过期        |
| 10015 | Api Key not found | Api Key 不存在 |
| 10016 | Invalid Signature | 签名错误        |
