签名方法
为了防止API调用过程中被恶意篡改,调用任何一个API都需要携带签名
- 密钥SecretKey在接入聚合平台时生成,可在商户后台的开发配置中修改
- 商户公钥在商户后台的开发配置中上传和修改,私钥接入方自行保存
- 平台公钥在接入聚合平台时同步给接入方
步骤一
接口请求参数进行Json封装,并做AES对称加密:
-
API接口参数组装成Json对象,如:
{"a":1, "b":"hello", "c":"world"} -
将Json对象转为字符串S,并对字符串S做UTF-8编码。当前仅支持UTF-8
-
使用密钥SecretKey与编码后的结果做AES对称加密,以base64编码输出
-
将base64编码的密文赋值给公共参数content
AES加解密示例
JAVA
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AesTool {
public static String encrypt(String str, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] result = cipher.doFinal(str.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(result);
}
public static String decrypt(String base64Str, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decoded = cipher.doFinal(Base64.getDecoder().decode(base64Str));
return new String(decoded, "UTF-8");
}
}
步骤二
签名算法(SHA256算法):
-
对所有API必填的公共参数(sign除外),根据参数名称的ASCII码表的顺序排序。如:
a=1, c=3, b=2排序后的顺序是a=1, b=2,c=3。 -
将排序好的参数名和参数值按url拼装方式拼接在一起,根据上面的示例得到的结果为:
a=1&b=3&c=2。 -
使用商户私钥,将拼装好字符串进行SHA256withRSA签名,签名后字节流以base64编码输出
-
将base64编码的签名赋值给公共参数sign
签名示例
JAVA
import java.security.spec.*;
import java.security.*;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
public class SignUtils {
public static String signSHA256(byte[] message, byte[] privateKeyBytes) throws Exception {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBytes));
KeyFactory keyf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyf.generatePrivate(priPKCS8);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
public static String makeSignStr(Map<String, String> params) {
List<String> keys = Lists.newArrayList();
for (Map.Entry<String, String> entry : params.entrySet()) {
if ("sign".equals(entry.getKey())) {
continue;
}
if (StringUtils.isNotBlank(entry.getValue())) {
keys.add(entry.getKey());
}
}
Collections.sort(keys);
List<String> temp = Lists.newArrayList();
for (String key : keys) {
String value = params.get(key);
temp.add(key + "=" + value);
}
return StringUtils.join(temp, "&");
}
public static void main(String[] args) {
try {
//读取私钥
byte[] privateKeyBytes = null;
Map<String, String> params = new HashMap<>();
params.put("mch_id", "商户编号");
params.put("app_id", "应用ID");
params.put("timestamp", "1541661668");
params.put("nonce_str", "aiz04enx0a2");
params.put("sign_type", "SHA");
params.put("version", "2.0");
params.put("content", "VBDExvz6/k56B1S5n7n3uOvI2sxZixcsV0Tdld92ym0CpnN8ooiCkXPgg0N1z8NC");
//签名
String sign = SignUtils.signSHA256(makeSignStr(params).getBytes("UTF-8"), privateKeyBytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}