2.3 签名机制

签名(Signature)的计算需要使用访问密钥对:appId/key系统分配

1. 签名生成的通用步骤如下:

第一步,设所有发送的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特别注意以下重要规则:

· 参数名ASCII码从小到大排序(字典序);

· 如果参数的值为空不参与签名;

· 参数名区分大小写;

第二步,对stringA进行HMAC-SHA256运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

举例:

假设传送的参数如下:

appId:21474836471

timeStamp:1626687341618

nonceStr:ibuaiVcKdpRxkhJA

第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:

stringA="appId=21474836471&nonceStr=ibuaiVcKdpRxkhJA&timeStamp=1626687341618";

第二步:拼接API密钥:

HMAC-SHA256签名方式:

    //注:HMAC-SHA256签名方式,部分语言的hmac方法生成结果二进制结果,需要调对应函数转化为十六进制字符串。

    String sign = SignUtils.HMACSHA256(secret,sortParams); 下面举例附java代码。

最终得到最终发送的数据:

appId=21474836471&nonceStr=ibuaiVcKdpRxkhJA&timeStamp=1626687341618&sign=D3E5169DDBC2EEBC1416ABABB7487AB3B91F897213E8B71278F1813DF35DD7F5

Java 代码示例(签名):

/** 生成签名**/

public static void main(String[] args) throws Exception {

//秘钥

    String secret= "nx8TkOYsG1an33DpeTlPav6BMgyHgmW1";

    Map<String,String> param = new HashMap<>();

    param.put("appId","21474836471");

    param.put("nonceStr","ibuaiVcKdpRxkhJA");

    param.put("timeStamp","1626687341618");

    //排序并拼接请求字符串

    String sortParams = sortParams(param);

//签名

    String sign = SignUtils.HMACSHA256(secret,sortParams);

    System.out.println(sign);

}

SignUtils 类:

import javax.crypto.Mac;

import javax.crypto.spec.SecretKeySpec;

import java.nio.charset.StandardCharsets;

import java.util.Comparator;

import java.util.List;

import java.util.Map;

import java.util.Objects;

import java.util.stream.Collectors;

/**

 * @Desc 签名工具类

 */

public class SignUtils {

    /**

     * 生成 HMACSHA256

     * @param data 待处理数据

     * @param key 密钥

     * @return 加密结果

     * @throws Exception

     */

    public static String HMACSHA256(String key, String data) throws Exception {

        Mac mac = Mac.getInstance("HmacSHA256");

        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");

        mac.init(secret_key);

        byte[] array = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));

        StringBuilder sb = new StringBuilder();

        for (byte item : array) {

            sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);

        }

        return sb.toString().toUpperCase();

    }

    /**

     * 组装拼接要发送的params

     */

    public static <R> String sortParams(Map<String, R> data) {

        //根据排序好的data

        List<Map.Entry<String, R>> sortedData = data.entrySet().stream().

                sorted(Comparator.comparing(Map.Entry::getKey)).collect(Collectors.toList());

        StringBuilder requestStr = new StringBuilder();

        //开始便利循环集合

        for (Map.Entry<String, R> k : sortedData) {

            //sign不需要进行签名

            if (Objects.equals(k.getKey(), "sign") || k.getValue() == null){

                continue;

            }else{

                requestStr.append(k.getKey()).append("=").append(k.getValue()).append("&");

            }

        }

        //去除最后一个多余的&符号

        return requestStr.substring(0,requestStr.length() -1);

    }

}