0%

接口签名笔记

####由于开放型的短信接口受到不明来源的恶意访问 大佬们紧急对整个项目的接口进行加密
原理:密钥+参数生成 md5 签名 客户端保存好密钥即可 之前对接苏宁易购的那个支付平台的时候 也是选择这种方式进行访问加密
1:生成密钥
这个随机定义 例如我选择使用 appId + salt 生成md5 截取前12位作为 指定app应用的密钥 如果发生泄漏 可以通过改变salt 来生成新的密钥
2:客户端密钥 存储
客户端 拿到密钥不能明文存储 例如 12位 可以拆分三段 然后分别进行偏移 这样如果别人要识别 必须反编译源码 并且读懂相关规则
3:签名规则
除开签名以外所有参数 按照排序规则排序 然后拼接上密钥 通过md5 或者类似的算法生成md5签名
4:服务端验证规则
除开签名 也按照客户端同样的排序规则排序 拼接密钥 通过相同的算法生成服务端签名 通过比较客户端签名 和服务端签名 来验证服务是否可靠、

生成签名 方法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env  groovy
/** groovy 生成md5 签名的算法
*
* @author ming
* @date 2017-11-15 12:45
*/
import java.nio.charset.Charset
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import org.apache.commons.codec.binary.Hex;


static void main(String[] args) {
Map<String, String[]> map = new HashMap<>()
map.put("sign", ["mkasfasfds"] as String[])
map.put("param", ["ming"] as String[])
map.put("b", ["b"] as String[])
map.put("a", ["a"] as String[])
print(getSign("ming", map))
}

/**
* 生成签名
* @param accessSecretKey 密钥
* @param parameterMap 参数map 集合
* @author ming
* @date 2017-11-15 11:42
*/
String getSign(String accessSecretKey, Map<String, String[]> parameterMap) {
//拼接参数
StringBuffer allNotEncryptString = new StringBuffer();
String signature = parameterMap.get("sign")[0];
// 获取未加密的请求参数
String notEncryptString = getRequestParameterString(parameterMap);
if (notEncryptString == null || notEncryptString.length() == 0) {
throw new NullPointerException(" 请求参数拼接为空")
}
// 组合所有加密的字符穿
allNotEncryptString.append(notEncryptString);
allNotEncryptString.append("secret" + accessSecretKey);

// 本地加密
String localSignature = md5(allNotEncryptString.toString(), Charset.forName("UTF-8")).toLowerCase();
return localSignature;
}

/**
* 取得未加密的请求参数字符串<br/>
* 注:1.该字符串的组成规则为:
* 1.1 请求参数 'key'+'value'成对
* 1.2 依据字母顺序表:a-->b-->c-->...-->y-->z 对‘请求参数key’排序
* 2.该字符串中不包括signature
*
*/
static String getRequestParameterString(Map<String, String[]> parameterMap) {
if (parameterMap == null || parameterMap.size() == 0) {
return null;
}
List<String> parameterNameList = new ArrayList<>(parameterMap.keySet());
// 对请求的key 排序
parameterNameList.sort { a, b -> (a <=> b) };
StringBuffer strBuffer = new StringBuffer();
for (String parameterName : parameterNameList) {
//排除签名
if (parameterName == "sign") {
continue;
}
String[] values = parameterMap.get(parameterName);
if (values != null) {
if (values.length == 1) {
strBuffer.append(parameterName + values[0]);
} else {
//此处规则 省略。。。。
}
}
}

return strBuffer.toString();
}

/***
* MD5加密 生成32位md5码
*
* @param inStr 待加密字符串
* @return 返回32位md5码
*/
static String md5(String inStr, Charset charset) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
if (messageDigest == null) {
throw new NullPointerException();
}
messageDigest.update(inStr.getBytes(charset));
byte[] digestBytes = messageDigest.digest();
return Hex.encodeHexString(digestBytes);
}