## 数字签名

---

签名方法对接口进行鉴权，所有接口每一次请求都需要包含签名信息（signature 参数），**以验证用户身份，防止信息被恶意篡改。**

### 申请安全凭证

在第一次使用 API 之前，需申请安全凭证，安全凭证包括 SecretId 和 SecretKey ，SecretId 是用于标识 API 调用者的身份，SecretKey 是用于加密签名字符串和服务器端验证签名字符串的密钥。SecretKey 必须严格保管，避免泄露。

### 签名生成算法

签名生成方法如下：

* 对所有请求参数（包括公有参数和私有参数，但不包括 signature 参数），按照参数名ASCII码表升序顺序排序。如：foo=1， bar=2， foo_bar=3， baz=4 排序后的顺序是 bar=2， baz=4， foo=1， foobar=3。
* 将排序好的参数名和参数值构造成字符串，格式为：key1+value1+key2+value2… 。根据上面的示例得到的构造结果为：bar2baz4foo1foobar3 。
* 选择与 secretId 配对的 secretKey ，加到上一步构造好的参数字符串之后，如 secretKey=6308afb129ea00301bd7c79621d07591 ，则最后的参数字符串为 bar2baz4foo1foobar36308afb129ea00301bd7c79621d07591。
* 把拼装好的字符串采用 utf-8 编码，使用 MD5 算法对字符串进行摘要，计算得到 signature 参数值，将其加入到接口请求参数中即可。MD5 是128位长度的摘要算法，用16进制表示，一个十六进制的字符能表示4个位，所以签名后的字符串长度固定为32位十六进制字符。


### 代码示例

```
/**
* 生成签名信息
* @param secretKey 产品私钥
* @param params 接口请求参数名和参数值map，不包括signature参数名
* @return
* @throws UnsupportedEncodingException
*/
public static String genSignature(String secretKey, Map<String, String> params) throws UnsupportedEncodingException {
    // 1. 参数名按照ASCII码表升序排序
    String[] keys = params.keySet().toArray(new String[0]);
    Arrays.sort(keys);

    // 2. 按照排序拼接参数名与参数值
    StringBuffer paramBuffer = new StringBuffer();
    for (String key : keys) {
        paramBuffer.append(key).append(params.get(key) == null ? "" : params.get(key));
    }
    // 3. 将secretKey拼接到最后
    paramBuffer.append(secretKey);

    // 4. MD5是128位长度的摘要算法，用16进制表示，一个十六进制的字符能表示4个位，所以签名后的字符串长度固定为32个十六进制字符。
    return DigestUtils.md5Hex(paramBuffer.toString().getBytes("UTF-8"));
}
```