外观
签名规则
约 1280 字大约 4 分钟
指南快速开始
2025-03-13
为保证接口交互的安全性,要求在请求时对请求内容进行签名。
基本约定
- 合作方订单号必须唯一。
- 业务平台对合作方返回内容为
json格式。 - 合作方传递参数的时候,不是必传的参数可以舍弃不传,不可为
null。 - 平台提供合作方标识
appId、privateKey,合作方需妥善保管。 - 如果合作方有白名单拦截,需对业务平台的出口IP加到网络白名单。
签名步骤
第一步
请求报文参数
data为集合M,将集合M内非空值的参数按照参数名ASCII码从小到大排序(字典序)。如果集合M内还存在一个参数节点的数据为集合N, 继续将集合N内非空参数值的参数按照参数名ASCII码从小到大排序(字典序)。依此类推,最终转为JSON字符串,得到待签名数据。特别注意以下重要规则:
- 所有参数名ASCII码从小到大排序(
字典序)。 - 如果参数的值为空(
null)不参与签名。 - 参数名
区分大小写。
例如
data为:{ "transactionId": "sd98214163746", "orderNo": "31886131315641561320532", "userPhone": "13700000000", "equityCode": "ASS205645", "ext": "{\"b\":\"1\",\"a\":\"1\"}" }则待签名字符串为:
{"equityCode":"ASS205645","ext":"{\"b\":\"1\",\"a\":\"1\"}","orderNo":"31886131315641561320532","transactionId":"sd98214163746","userPhone":"13700000000"}- 所有参数名ASCII码从小到大排序(
第二步
使用平台提供的私钥,根据
PKCS8编码密钥规范产生私钥对象,然后使用私钥对象通过SHA256算法对待签名数据进行签名,得到签名数据。Demo.javavoid orderSyncBatch() throws Exception { // POST请求body OpenHandleParam<HandleOrderParam> body = new OpenHandleParam<>(); // 具体的业务数据data HandleOrderParam data = new HandleOrderParam(); data.setPhone("15811778899"); data.setProductId("ASDFQ2W34"); data.setAdPlatform("XXSenIns公众号"); data.setAppName("微信"); data.setSnapshotUrl("http://localhost"); data.setImageUrls("http://localhost"); data.setSourceUrl("http://localhost"); data.setChannelTxId("123456781"); data.setMobileOrderId("123456781"); data.setMobileOrderTime("20240102102533"); // 使用工具对参数排序处理 TreeMap<String, Object> map = JsonSortUtil.jsonToMap(JSONUtil.toJsonStr(data)); // 得到签名 String sign = RsaSignUtil.sign(JSONUtil.toJsonStr(map), privateKey); // 设置body的参数值 body.setAppId(appId).setSign(sign).setData(data); // 调用请求 doPost(body); }SignUtil.java// 签名工具类 public class SignUtil { // 签名算法名称 private static final String RSA_KEY_ALGORITHM = "RSA"; // 标准签名算法名称 private static final String RSA2_SIGNATURE_ALGORITHM = "SHA256withRSA"; /** * RSA签名 * * @param data 待签名数据 * @param priKey 私钥 * @return 签名 */ public static String sign(String data, String priKey) throws Exception { // 创建PKCS8编码密钥规范 PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(priKey)); // 返回转换指定算法的KeyFactory对象 KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); // 根据PKCS8编码密钥规范产生私钥对象 PrivateKey privateKey = keyFactory.generatePrivate(pkcs8KeySpec); // 用指定算法产生签名对象Signature Signature signature = Signature.getInstance(RSA2_SIGNATURE_ALGORITHM); // 用私钥初始化签名对象Signature signature.initSign(privateKey); // 将待签名的数据传送给签名对象(须在初始化之后) signature.update(data.getBytes(StandardCharsets.UTF_8)); // 返回签名结果字节数组 byte[] sign = signature.sign(); // 返回Base64编码后的字符串 return Base64.getEncoder().encodeToString(sign); } /** * RSA校验数字签名 * * @param data 待校验数据 * @param sign 数字签名 * @param pubKey 公钥 * @return boolean 校验成功返回true,失败返回false */ public static boolean verify(String data, String sign, String pubKey) throws Exception { // 返回转换指定算法的KeyFactory对象 KeyFactory keyFactory = KeyFactory.getInstance(RSA_KEY_ALGORITHM); // 创建X509编码密钥规范 X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(pubKey)); // 根据X509编码密钥规范产生公钥对象 PublicKey publicKey = keyFactory.generatePublic(x509KeySpec); // 用指定算法产生签名对象Signature Signature signature = Signature.getInstance(RSA2_SIGNATURE_ALGORITHM); // 用公钥初始化签名对象,用于验证签名 signature.initVerify(publicKey); // 更新签名内容 signature.update(data.getBytes(StandardCharsets.UTF_8)); // 得到验证结果 return signature.verify(Base64.getDecoder().decode(sign)); } }JsonSortUtil.java// 排序工具类 public class JsonSortUtil { /** * JSON转顺序排序的Map(为了方便后期获取数据,应答就不返回JSON字符串了,可自行去转换) * * @param jsonStr 原始json * @return 响应的map */ public static TreeMap<String, Object> jsonToMap(String jsonStr) { TreeMap<String, Object> treeMap = new TreeMap<>(); JSONObject json = JSONObject.parseObject(jsonStr);// Feature.OrderedField实现解析后保存不乱序 Iterator<String> keys = json.keySet().iterator(); while (keys.hasNext()) { String key = keys.next(); Object value = json.get(key); // 判断传入kay-value中是否含有""或null if (json.get(key) == null || value == null) { // 当JSON字符串存在null时,不将该kay-value放入Map中,即显示的结果不包括该kay-value continue; } // 判断是否为JSONArray(json数组) if (value instanceof JSONArray jsonArray) { List<Object> arrayList = new ArrayList<>(); for (Object object : jsonArray) { // 判断是否为JSONObject,如果是 转化成TreeMap if (object instanceof JSONObject) { object = jsonToMap(object.toString()); } arrayList.add(object); } treeMap.put(key, arrayList); } else { // 判断该JSON中是否嵌套JSON boolean flag = isJSONValid(value.toString()); if (flag) { // 若嵌套json了,通过递归再对嵌套的json(即子json)进行排序 value = jsonToMap(value.toString()); } // 其他基础类型直接放入treeMap // JSONObject可进行再次解析转换 treeMap.put(key, value); } } return treeMap; } /** * 校验是否是JSON字符串 * * @param json 传入数据 * @return 是JSON返回true, 否则false */ public static boolean isJSONValid(String json) { if (Objects.isNull(json) || "null".equals(json) || json.isEmpty()) { return false; } try { JSONObject.parseObject(json, JSONObject.class); } catch (JSONException ex) { return false; } return true; } }完成
工具类下载
Java工具类:
Python示例:
C# 示例