1.
签名字符串为除了sign、x-nexus-api-key、versions之外的其他非空字段组成键值对;
2.
所有参与签名的字段,按字段名的ASCII码从小到大排序后,使用URL的键值对的格式(即key1=value1&key2=value2)拼接 成字符串string
如
appId=companyAppId001
nonce=V6BC6WHMU1D2NGT17D959C4W6RQP3I0D
timestamp=20250421111104
userCode=54
排序后的字符串:
string="appId=companyAppId001&nonce=V6BC6WHMU1D2NGT17D959C4W6RQP3I0D×tamp=20250421111104userCode=54"
3.
用商户的RSA私钥进行签名;
RSAUtils.sign(string, cusRsaPrivateKey)
如测试私钥
cusRsaPrivateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCKu0DkMEGg4oRuVxL/vA8rhltFd+iN+T48VtyG/fM4qI0UaDixYeSBr78z/6hoBfqomUzFxlKhtQvR0gXN1NWH47wLH9yMJ7nqBtsgGfnfjFCKusmbnw+DpK2EcQfftEgbtRpp/WlUW2dlCt3uSa0Rkf8anbMYYYics7xu3VkBjo/CFMlP/yAf6KrmgBQ/tfmydOFPsQ3FgP9Wi/v4bEZkSjzBiLMzNaiXVMw1Uh4VjWXoz6ZtiDjE4xmL6Z7oukkYkZZwP00v0LQS4uyWHdr40rbfqYed3oMoZzIqXH+xaATFQrAfkGgBb8VSPdl0UzrH6iPHN5HBs6IAX95dS9o1AgMBAAECggEAUXkW6uvMHwfobkSEy9tNrWoCQBHjaT1u51C7witxo1yZPdrXMJUk9WSZDZie9jhMtKcTBzMpY/5HKroE3pSXsm50CLR1eXn7HQ1ZssiOjlKBGngH3nTTT6RuotEepqnhjyQUlxpTuU7Usepn24E5OpBbGO50N2VQLXcAursTgm2xaKCm8/dC17kpmeNT53bAECuFWhA2VvfeEtjgkafLdEZDnazOxAaRPha9IT64e2D0JiW9ituFBwJELcE6vZ2Av61onGtq8QZ7eAOqhzJ2Pfgxxhxk6HGJu1CCkWmXEscE8+kEMqLt0ziCCXRAniFtImmdCVzTWG91asktcAB/AQKBgQDAgAMYJizYsSDv7TK+67bul4au6RsBcD9vxICcX9OyFk5o3ucmFrYgPe1+BH1/3b42vTLjnAy0mhVZgiFNPqVuaX6OsTcX+6D8ndkS95csBN1oDJ1ElQ1TZGKnJ0FZGualLZTx4szZebR46RFVOD1fS30191SKYPlywvYV2QkQfQKBgQC4fqnF4pVOhEA+z7YRaWw8fhe33vdsHnXVhoEpJNvblLCxpOcs5ksjN3RtX2sI/9JGDqcc2RWrohu8oe5cojRdwyW0niyAkyjE7f5kiP7aDSM7JcAmRKMXTzQuaVnG/b+GrvIQ2bCp5KZZ93j3sAT3tzt/zefW+UQELUys1sCWGQKBgQCpOZ/+wciY8hX/dakvt64IT0LCTwBDonbR/ZAGaCOFIiDqBlJz5HFVJLu8FZxyJPBL1MWA3DU7K11rY399FX5+8tSkAlxpg/bBPM6Q4wGNW/V7u+MhYFrnnY8wXM0Q1Ro/yTNv9S6aXGhwBowLO7aQKJ+5KmWyhI+l+Ig3KqXSTQKBgQCDy7A6NcgVQ/KlLDrTK21vtV/6MSStLwFcXO6t1q/cp9alwLiIYadxa+8XJt/OmPm8pEELIaUSbAbGdSNTN2iTRpIL+iauXvHz5FQju53bZSEy4p/mdofPYfxT31yMA78rVpSEJw7F/xo7EMdwoWSbFWFCxnQFJSJKnZW1rt/7GQKBgFq1PPIWoPvYbMpMMWyzQbQtXHt6CxA9PDi8ZGslxQHBfsl1WXieEGP/JUXcZchLyHFzFvvQf3uK2EkWwjqeIrJF4VrMEMLpdIjR9fhnhLd6j9M52iLhu/q05W3xkU54oF9iD83KHtzqt5qOkSW6PBoBR3BwFyT+bAfg4JJYB8BD";
则上面字符串加密后的
sign="DYtffEz/KTeHDOtG47pBbNntgJJEM2akmPRS2w9V43HDTSW5S52d8/k7596cO6/MF/IeEus3o9NLtqKMFwrZc0ju/qYHaSJbYL/YnPgs3peoI8xMTobge2bvIeEdbQXNxb0yxCQe5Im8Ndi5fITTfdFcz1ZM8+4T9M6MSDBnjfdpHpGV/KSgRFAeh71kP2PFYzt/g0NNqWVbeJCJVvStdYYumMis9fnY+quyD1XF8V+gymaQQWIxDKBXbMSpJuOgxjVuHE0jIe+x2dP8PNC/BlxspAFbdv1SRK7qOz7Ph83NPETm2RRigIA2SxLEw+ITC7PfI5kI6+PpZj29wk0KsQ==";
4.
请求接口成功后Nexus系统返回JSON字符串数据;
所有参数(sign、x-nexus-api-key、versions字段除外)只要传了值(不包括空字符串)都要参与签名,否则验签无法通过!含MAVEN-JAR包版本
https://oss.tevaupay.com/tevau-nexus-test/Public_catalogue/demo.zip
Request Body
{
"orderId": "12345",
"eventType": "UsdtDeposit",
"tradeStatus": "Success"
}
要确保传入 Webhook 请求的真实性,您应该验证 "x-timestamp" 和 "x-signature" 标头中包含的数字签名。以下是如何在 JAVA 中执行此作的示例: private static final String RSA = "RSA";
private static final String SIGN_ALGORITHMS = "SHA1WithRSA";
public static void main(String[] args) throws Exception {
//Post所有请求参数
String body = "{\"orderId\":\"12345\",\"eventType\":\"UsdtDeposit\",\"tradeStatus\":\"Success\"}";
//Headers中x-timestamp
String timestamp = "20250903140909";
//Headers中x-signature
String signature = "rePiuSTsPvgedzcAAAd7UjW31buTfSg2oJbgyD1s7ygVX/e9mAHw77sLsNjZEq8M3YgkWh2RCS/UCTeLP2+KLnx930izX38EyLuQYU8ewioDfD43ZIp3HCLP/g5cY7DcZyE0S68wydfi5FmsI9dixO47yMRh+oxCz+w4QBteYw0zxxApdEwTHtOnpTT5VIwCtZwBmaYJjMdJ9WHjrD1X/owt65oi5famgMLcQkz3uoMq8Cwv0od1zqm5YCWw0tyn8xTAUQ83cz0AWwIAyhrEXrhLdm4K4rqUrRnKH9U4xzfGncnH1KhziNtJTVPXB2l3BVZ5qsVO0lUOkIF/fs7QdA==";
//WebHook验签公钥
String publicKey = "";
String data = "timestamp=" + timestamp + getSortedString(body);
boolean verifyResult = verify(data, signature, publicKey);
System.out.printf("打印验签结果:" + verifyResult);
}
/**
* 公钥验签
*
* @param data
* @param sign
* @param publicKey
* @return
* @throws Exception
*/
public static boolean verify(String data, String sign, String publicKey) throws Exception {
byte[] keyBytes = java.util.Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA);
PublicKey publicK = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
signature.initVerify(publicK);
signature.update(data.getBytes());
return signature.verify(Base64.getDecoder().decode(sign));
}
/**
* getSortedString 对参数按照Key进行ASCII排序
*
* @param requestBodyStr 请求参数
* @return 排序拼装后的字符串
*/
public static String getSortedString(String requestBodyStr) {
if (jodd.util.StringUtil.isEmpty(requestBodyStr)) {
return "";
}
com.aliyun.openservices.shade.com.alibaba.fastjson.JSONObject jsonObject = com.aliyun.openservices.shade.com.alibaba.fastjson.JSON.parseObject(requestBodyStr);
SortedMap<String, Object> sortMap = new TreeMap<>();
StringBuffer sbf = new StringBuffer();
for (Map.Entry<String, Object> objectEntry : jsonObject.entrySet()) {
String key = objectEntry.getKey();
Object value = objectEntry.getValue();
if ("sign".equals(key)) {
continue;
}
sortMap.put(key, value);
}
Set mapEntrySet = sortMap.entrySet();
Iterator it = mapEntrySet.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v = entry.getValue();
sbf.append(k + "=" + v + "&");
}
String sbfString = sbf.toString().replaceAll("\"", "");
return sbfString.substring(0, sbfString.length() - 1);
}
Modified at 2026-02-25 11:23:10