自行实现验签
此处主要介绍同步返回和异步通知下公钥与证书的验签方法。
同步返回验签
说明:
• xxx_response 的 JSON 值内容需要包含首尾的 { 和 } 两个尖括号,双引号也需要参与验签。
• 如果字符串中包含 http:// 的正斜杠,需要先将正斜杠做转义,默认打印出来的字符串是已经做过转义的。建议验签不通过时将正斜杠转义一次后再做一次验签。
1. 获取响应值
{
"yocyl_basic_employee_querydetail_response": {
"code": "0",
"msg": "success",
"employee": {
"deptId": "1595233495325913090",
"workCode": "",
"name": "张三",
"sex": 1,
"birthday": "",
"age": null,
"joinedDate": "1980-06-16",
"workAge": null,
"idNo": "255255436155",
"phone": "18682723920",
"email": "k.fvqte@qq.com",
"country": "",
"bank": "XX银行",
"account": "",
"bankLocationCode": "XBC",
"loginName": "老张",
"isLoginUser": false,
"outEmployeeId": "1212121",
"outDeptId": "",
"sourceSystem": ""
}
},
"sign": "BQWPeP2Rv8q70BPPL+CN8XA3BQg7ekwkvL1U9aXMoe9UfsUKRAkjA/aFFLBrJEWopZX/m+dyEDJCvdtxyEzdOBH/3EF7hhM6Ymb3qudIrsHZp7N2TZSI5LO2ud9P7BYGL6mo5B2gLnnBjAbW66k59iGBGGQrwzyfbwaqgrgmucHaVbITv1WXHIiLkWf7zxCk8iu2qTO5/TZcafgs875OTpw0P3I5HOzTGeMHV/68u29/7YhXxG+oMXiObb+osP4ilx+/QNt/YC//E9ZnFrs7fwRVWGRztNhUU5mGjDxVPGbHpZqT0tD4GgkoZ5McX42I0bQarh4hMAUE0hweYI72tg=="
}
2. 待验签字段
{
"code": "0",
"msg": "success",
"employee": {
"deptId": "1595233495325913090",
"workCode": "",
"name": "张三",
"sex": 1,
"birthday": "",
"age": null,
"joinedDate": "1980-06-16",
"workAge": null,
"idNo": "255255436155",
"phone": "18682723920",
"email": "k.fvqte@qq.com",
"country": "",
"bank": "工商银行",
"account": "",
"bankLocationCode": "ICBC",
"loginName": "老张",
"isLoginUser": false,
"outEmployeeId": "1212121",
"outDeptId": "",
"sourceSystem": ""
}
}
3.取出签名值 sign
BQWPeP2Rv8q70BPPL+CN8XA3BQg7ekwkvL1U9aXMoe9UfsUKRAkjA/aFFLBrJEWopZX/m+dyEDJCvdtxyEzdOBH/3EF7hhM6Ymb3qudIrsHZp7N2TZSI5LO2ud9P7BYGL6mo5B2gLnnBjAbW66k59iGBGGQrwzyfbwaqgrgmucHaVbITv1WXHIiLkWf7zxCk8iu2qTO5/TZcafgs875OTpw0P3I5HOzTGeMHV/68u29/7YhXxG+oMXiObb+osP4ilx+/QNt/YC//E9ZnFrs7fwRVWGRztNhUU5mGjDxVPGbHpZqT0tD4GgkoZ5McX42I0bQarh4hMAUE0hweYI72tg==
4. 调用验签函数
异步通知验签
1. 获取异步通知
{
"tenantId": "000001",
"bizBatchNo": "PV-202305-000061",
"bizFlowNo": "PV-202305-0000611696552451416538112",
"bizNoteCode": "PV-202305-000061",
"bizSystemCode": "XX_XX_GXP",
"reconcileCode": "S00001",
"askNumber": "PV-202305-0000611696552451416538112",
"paySerialNumber": "PV-202305-0000611696552451416538112",
"payState": 2,
"payResultMsg": "已支付",
"bankDealResult": "已支付",
"payFinishDatetime": "2023-05-30 19:12:00",
"bankCardNum": "888888",
"holdName": "XX银行01",
"source": 1,
"payBankCode": "CXB",
"payOrgCode": "01",
"realPayCurrencyCode": "CNY",
"realPayAmount": 878888.00,
"realPayOrgId": 1660522828064256002,
"payableCurrencyCode": "CNY",
"payableAmount": 878888.00,
"payBankLocation": "XX银行股份有限公司XX分行",
"payBankCnaps": "308100005019",
"costBearWay": "SHAR",
"costCurrency": "CNY",
"recAccountNumber": "666666699999999",
"recAccountName": "专家",
"recBankCode": "XBC",
"recBankName": "中国XX银行",
"recBankLocation": "中国XX银行XX分行营业管理部",
"recCnaps": "001110012009",
"bizScene": 1,
"createTime": "2023-05-30 19:07:29",
"signType":"RSA2",
"payBankSubject": "",
"bankSerialNumber": null,
"payResultCode": null,
"sourceHandlingCode": null,
"createUserName": null,
"createUserAccount": null,
"payableNoteCode": null,
"otherPayWayInfo": null,
"collectionId": null,
"refundDatetime": null,
"cmbPayLineNumber": null,
"originalNoteCode": null,
"bizTypeCode": null,
"refundExpenditureAccountTransId": null,
"recSwiftCode": null,
"payResultNotifyType": null,
"commission": null,
"genPacketState": null,
"sign":"VOxZnKR3v8OaT2q0kq9BIcZ+6gV8+7cX6Y7yS8ickbn6cppZGSAAUkaD4k83IK8fgSWWef9vLrKWOwwd96b3YmBQMrzvZWz4llV8SMoaWrZvR9Z6DAQeJW3TxjDbEDC8lUozb/sFjaR84JovgUFa2mJ+qJpMTuJJpttCE+Qp38emXe5swqlktCwDk9dTHD9LXEL7n+IRhmJU2NwyurZ3bY8Nezr7fCRLDn3MayMj5+xz8OwCw8WF4kSfW0BC/On1ZeM2cO9K6qjV4mFXp0p3Az6cUvFniFBA4Lli8hgHh8PUm2ydW4S5Xm48dIgyL0OUNpjkHdGyFlzikRvXacSl8Q==",
"paymentExtension": {
"actualExchangeRate": null,
"actualExchangeAmount": null
}
}
2. 获取待验签参数
1.
2.
3. 获取签名值 sign
4. 调用验签函数
示例代码
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* 验签工具DEMO
*
* @author Stewart
* @version UnsignUtil v1.0 2023/6/2
*/
public class UnsignUtil {
/**
* 财资云平台公钥
*/
private static final String platformPublicKey = "公钥字符串";
/**
* 验签方法入口
*
* @param requestParam 请求参数
* @return 验签结果
* @throws Exception 验签过程中抛出的异常
*/
private Boolean checkRsa(Map<String, String> requestParam) throws Exception {
String charset = requestParam.get("charset");
String sign = requestParam.get("sign");
String signType = requestParam.get("signType");
String content = getSignCheckContent(requestParam);
return doVerify(content, charset, platformPublicKey, sign, signType);
}
/**
* 获取验签内容
*
* @param params 参数
* @return 验签参数拼接字符串
*/
public static String getSignCheckContent(Map<String, String> params) {
if (params == null) {
return null;
} else {
params.remove("sign");
StringBuilder content = new StringBuilder();
List<String> keys = new ArrayList(params.keySet());
Collections.sort(keys);
for(int i = 0; i < keys.size(); ++i) {
String key = keys.get(i);
String value = params.get(key);
content.append(i == 0 ? "" : "&").append(key).append("=").append(value);
}
return content.toString();
}
}
/**
* 进行校验
*
* @param content 验签内容
* @param charset 字符集
* @param publicKey 财资云平台公钥
* @param sign 加签字符串
* @return 验签结果
* @throws Exception 验签过程中发生的异常
*/
protected static boolean doVerify(String content, String charset, String publicKey, String sign, String signType ) throws Exception {
PublicKey pubKey = getPublicKey("RSA", publicKey, charset);
Signature signature = Signature.getInstance(getSignAlgorithm(signType));
signature.initVerify(pubKey);
if (hasText(charset)) {
signature.update(content.getBytes(charset));
} else {
signature.update(content.getBytes());
}
return signature.verify(Base64.decodeBase64(sign.getBytes(hasText(charset) ? charset : Charset.defaultCharset().name())));
}
/**
* 获取公钥对象
*
* @param algorithm 加签方式
* @param publicKey 财资云平台公钥
* @param charset 字符集
* @return 公钥对象
* @throws NoSuchAlgorithmException 算法不存在异常
* @throws InvalidKeySpecException 密钥不合法异常
* @throws UnsupportedEncodingException 不支持的字符集异常
*/
public static PublicKey getPublicKey(String algorithm, String publicKey, String charset) throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
byte[] encodedKey = Base64.decodeBase64(publicKey.getBytes(charset));
return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
}
/**
* 获取算法名
*
* @param signType 加签类型
* @return 算法名
*/
protected static String getSignAlgorithm(String signType) {
switch (signType){
case "RSA2":
return "SHA256WithRSA";
case "RSA":
return "SHA1WithRSA";
default:
return null;
}
}
/**
* 是否有内容
*
* @param str 字符串
* @return 是否有内容
*/
public static boolean hasText(CharSequence str) {
return isEmpty(str) && containsText(str);
}
/**
* 是否包含内容
*
* @param str 字符串
* @return 是否有内容
*/
private static boolean containsText(CharSequence str) {
long count = str.chars().filter(chr -> !Character.isWhitespace(chr)).count();
if(count>0){
return true;
}
return false;
}
/**
* 判断字符串是不是空字符串
*
* @param str 字符串
* @return 是否为空
*/
public static boolean isEmpty(CharSequence str) {
return str == null || "".contentEquals(str);
}
}
信息
修改于 2023-06-17 01:16:36