验签方法
为了防止API返回过程中被恶意篡改,调用方需对返回值做签名验证,并解密获取到返回值内容
- 密钥SecretKey在接入聚合平台时生成,可在商户后台的开发配置中修改
- 商户公钥在商户后台的开发配置中上传和修改,私钥接入方自行保存
- 平台公钥在接入聚合平台时同步给接入方
步骤一
签名验证算法(SHA256算法):
-
对所有返回的不为空的公共参数(sign除外),根据参数名称的ASCII码表的顺序排序,以url拼装方式把参数名和参数值拼接在一起。 如:
code=0&msg=成功&result=VBDExvz6/k56B1S5n7n3uOvI2sxZixcsV0Tdld92ym0CpnN8ooiCkXPgg0N1z8NC&sign_type=SHA -
使用平台公钥,将拼装好字符串与sign值进行SHA256withRSA验证
签名验证示例
JAVA
import java.security.spec.*;
import java.security.*;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
public class SignUtils {
public static boolean verifySHA256(byte[] message, byte[] publicKeyBytes, String sign) throws Exception {
X509EncodedKeySpec PubKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBytes));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(PubKeySpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(message);
return signature.verify(Base64.getDecoder().decode(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[] publicKeyBytes = null;
String sign = "xxxxxxxx";
Map<String, String> params = new HashMap<>();
params.put("code", "0");
params.put("msg", "SUCCESS");
params.put("sign_type", "SHA");
//返回失败时可能不返回result,需根据实际返回情况而定
params.put("result", "VBDExvz6/k56B1S5n7n3uOvI2sxZixcsV0Tdld92ym0CpnN8ooiCkXPgg0N1z8NC");
//签名验证
boolean isMatch = SignUtils.verifySHA256(makeSignStr(params).getBytes("UTF-8"), publicKeyBytes, sign);
} catch (Exception e) {
e.printStackTrace();
}
}
}
步骤二
当接口成功返回(code=0)时,将result字段做AES对称解密,即可获取接口返回的具体参数。
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");
}
}