# 公共签名

## 系统级 Header

* 【必选】X-Sy-Key：AppKey &#x20;
* 【必选】X-Sy-Signature：签名字符串 &#x20;
* 【必选】X-Sy-Timestamp：API调用者传递时间戳，值为当前时间的秒数，也就是从1970年1月1日起至今的时间转换为秒，时间戳有效时间为15分钟。 &#x20;
* 【必选】X-Sy-Nonce：API调用者生成的 UUID，结合时间戳防重放。

## 请求签名

对于每一次HTTP或者HTTPS协议请求，我们会根据访问中的签名信息验证访问请求者身份。具体由使用appKey和appSecret对称加密验证实现。其中appKey是访问者身份，appSecret是加密签名字符串和服务器端验证签名字符串的密钥，必须严格保密谨防泄露。

### 1. 指定请求参数

在代码中指定请求参数，参数中需要包含系统级Header和接口必备的参数信息。

**说明：** 请求参数中不允许出现以signature为key的参数。

示例代码如下：

```
String appKey = "testKsy";
String appSecret = "testSecret";
String timestamp = String.valueOf(System.currentTimeMillis()/1000);
String signNonce = java.util.UUID.randomUUID().toString().replaceAll("-", "");
java.util.Map<String, String> paras = new java.util.HashMap<String, String>();
```

指定参数：

```
paras.put("appKey", testKey);
paras.put("timestamp", timestamp);
paras.put("signNonce", signNonce);

paras.put("name", "okok");
paras.put("mobile", "0999999999");
paras.put("credential_no", "1111581111");
```

去除签名关键字Key：

```
if (paras.containsKey("signature"))
paras.remove("signature");
```

### 2. 根据参数Key排序（升序）

参考代码如下：

```
java.util.TreeMap<String, String> sortParas = new java.util.TreeMap<String, String>(paras);
```

### 3. 构造待签名的请求串

在一般的URLEncode后再增加三种字符替换：加号`（+）`替换成 `%20`、星号`（*）`替换成 `%2A`、`%7E` 替换回波浪号`（~）`参考代码如下：

```
public static String specialUrlEncode(String value) throws Exception {
    return java.net.URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
```

构造待签名的请求串：

1. 把排序后的参数顺序拼接成如下格式：

   ```
   * specialUrlEncode(参数Key) + "=" + specialUrlEncode(参数值)
   ```

   参考代码如下：

   ```
   java.util.Iterator<String> it = sortParas.keySet().iterator();
   StringBuilder sortQueryString = new StringBuilder();
   while (it.hasNext()) {
       String key = it.next();
       sortQueryString.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
   }
   String sortedQueryString = sortQueryString.substring(1); //去除第一个多余的&符号
   ```

### 4. 签名

签名采用HmacSHA1算法 + Base64，编码采用UTF-8。参考代码如下：

```
String sign = sign(appSecret, sortedQueryString);

public static String sign(String appSecret, String stringToSign) throws Exception {
    javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA1");
    mac.init(new javax.crypto.spec.SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA1"));
    byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
    return new sun.misc.BASE64Encoder().encode(signData);
}
```

参数说明：

1. `appSecret`：你的`appKey`对应的秘钥`appSecret` &#x20;
2. `stringToSign`：即第三步生成的待签名请求串

### 5. 增加签名结果到请求参数中，发送请求

**说明：** 签名也要做特殊URL编码。

```
String signature = specialUrlEncode(sign);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.external.whcash.com/master.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
