签名(Signature)的计算需要使用访问密钥对:appId/key(系统分配)
第一步,设所有发送的数据为集合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);
}
}