签名规则说明
- sign算法说明:
- 为空的参数不参数与签名
- 参数按键值+数值的ASCII编码顺序拼接,生成signData。使用RSA私钥,通过SHA1withRSA算法,RSA加密统一私钥加密,公钥验签。
- RSA公私钥可以通过https://www.bejson.com/enc/rsa/ 获取(密钥长度选择1024bit,生成的公钥是以MIG开头,AB结尾),或者自己生成密钥
signData示列
- amount=100&bankCode=ICBC&bankcardAccountName=张三&bankcardAccountNo=123456789123456789&merchantId=80008&merchantOrderNo=20230630202003¬ifyUrl=http://www.baidu.com&version=1.0.1
RSA私钥加密signData示列
- kqzun1v2Iw1nAr9bYzKsRuwKupKXppwNFVW34sLqCD7GmuXAaw1jvcXqBhDD9camhdt/1Yq1D7AyDWDOg60DHAigrbULuek+ZlwiRRiFJOVZTbJRnkewmfDXCVbPOfIGf0NTFjxcJqabL/5SKngwDEGav65+NlJhtgHGfo5UvoI=
加密验签代码参考(JAVA)
-
# 请求参数
Map<String, String> params = new TreeMap<>();
params.put("merchantOrderNo", "20230630202003");
params.put("amount", "100");
params.put("bankCode", "ICBC");
params.put("bankcardAccountNo", "123456789123456789");
params.put("bankcardAccountName", "张三");
params.put("notifyUrl", "https://www.baidu.com");
params.put("merchantId", Const.MERCH_ID);
params.put("version", Const.VERSION);
String signData = data.entrySet().stream().filter(e -> StringUtils.isNotEmpty(e.getValue())).map(e -> e.getKey().concat("=").concat(e.getValue())).collect(Collectors.joining("&"));
params.put("sign", RSAUtils.sign(signData, privateKey));
HttpUtils.post(url, JSON.toJSONString(params));
# RSAUtils工具类
import com.alibaba.fastjson.JSONObject;
import com.demo.Const.Const;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* 工具类
*/
public class RSAUtils {
/**
* RSA公钥转换
* @param publicKeyStr 公钥字符串
* @return 公钥
* @throws
*/
public static PublicKey parsePublicKey(String publicKeyStr) throws InvalidKeySpecException {
PublicKey publicKey = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.getDecoder().decode(publicKeyStr.getBytes(StandardCharsets.UTF_8));
publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
} catch (NoSuchAlgorithmException e) {
}
return publicKey;
}
/**
* RSA私钥转换
*
* @param privateKeyStr 私钥字符串
* @return 私钥
* @throws
*/
public static PrivateKey parsePrivateKey(String privateKeyStr) throws InvalidKeySpecException {
PrivateKey privateKey = null;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = Base64.getDecoder().decode(privateKeyStr.getBytes(StandardCharsets.UTF_8));
privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
} catch (NoSuchAlgorithmException e) {
}
return privateKey;
}
/**
* 签名(使用商户私钥)
* @param data 待签名数据
* @param privateKey 私钥
* @return
*/
public static String sign(String data, String privateKey) throws Exception {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(parsePrivateKey(privateKey));
signature.update(data.getBytes("UTF-8"));
return new String(org.apache.commons.codec.binary.Base64.encodeBase64(signature.sign()));
}
/**
* 验签(使用平台公钥)
* @param data
* @return
* @throws Exception
*/
public static boolean verify(JSONObject data) throws Exception {
String sign = data.remove("sign").toString();
Map<String,String> map = JSONObject.toJavaObject(data, Map.class);
String signData = new TreeMap<>(map).entrySet().stream().filter(e -> StringUtils.isNotEmpty(e.getValue())).map(e -> e.getKey().concat("=").concat(e.getValue())).collect(Collectors.joining("&"));
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(parsePublicKey(Const.PUBLIC_KEY));
signature.update(signData.getBytes("UTF-8"));
return signature.verify(org.apache.commons.codec.binary.Base64.decodeBase64(sign.getBytes("UTF-8")));
}
}
加密验签代码参考(PHP)
-
# 请求参数
$arr["merchantId"] = Config::merchNo; //商户号
$arr["version"] = Config::version; //版本号
$arr["merchantOrderNo"] = date("YmdHis",time()).rand(1000,10000); //商户订单号
$arr["amount"] = "100"; //金额 元
$arr["bankCode"] = "ICBC";//银行代码
$arr["bankcardAccountNo"] = "123456789";//银行卡号
$arr["bankcardAccountName"] = "张三";//持卡人姓名
$arr["notifyUrl"] = "https://baidu.com";//回调地址
//排序
ksort($arr);
//拼接签名字符串
$signData = ServiceUtil::get_sign($arr);
//获取私钥
$privateKey = ServiceUtil::privateKeyStr(Config::privateKey);
//生成签名
$sign = ServiceUtil::sign($signData,$privateKey);
$arr["sign"] = $sign;
$reqData = json_encode($arr, JSON_UNESCAPED_UNICODE);
$result = ServiceUtil::curlPost(Config::remitUrl, $reqData);
# 工具类(加密验签)
header("Content-type:text/html;charset=utf8");
class ServiceUtil
{
/**
* 数组转为url字符串 过滤掉空值字段
* $arr 数组
*/
static function get_sign($arr)
{
$signmd5 = "";
foreach ($arr as $x => $x_value) {
if ($x_value == "0" || !empty($x_value)) {
if ($signmd5 == "") {
$signmd5 = $signmd5 . $x . '=' . $x_value;
} else {
$signmd5 = $signmd5 . '&' . $x . '=' . $x_value;
}
}
}
return $signmd5;
}
/**
* 拼接公钥字符串
*
*/
static function publicKeyStr($publicStr)
{
//公钥
$public_key = "-----BEGIN PUBLIC KEY-----\r\n";
foreach (str_split($publicStr, 64) as $str) {
$public_key .= $str . "\r\n";
}
$public_key .= "-----END PUBLIC KEY-----";
return $public_key;
}
//拼接私钥字符串
static function privateKeyStr($privatekey)
{
$private_key = "-----BEGIN PRIVATE KEY-----\r\n";
foreach (str_split($privatekey, 64) as $str) {
$private_key .= $str . "\r\n";
}
$private_key .= "-----END PRIVATE KEY-----";
return $private_key;
}
/**
* 平台公钥验签
* @param $arr
* @param $sign
* @param $path
* @return int
* @throws Exception
*/
static function verify($plainText, $sign, $path)
{
$resource = openssl_pkey_get_public($path);
$result = openssl_verify($plainText, base64_decode($sign), $resource);
openssl_free_key($resource);
if (!$result) {
return;
}
return $result;
}
/**
* 商户私钥签名
* @param $plainText
* @param $path
* @return string
* @throws Exception
*/
static function sign($plainText, $path)
{
try {
$resource = openssl_pkey_get_private($path);
$result = openssl_sign($plainText, $sign, $resource);
openssl_free_key($resource);
if (!$result) {
return;
}
return base64_encode($sign);
} catch (Exception $e) {
return;
}
}
//发起请求
static function curlPost($url, $data)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$tmpInfo = curl_exec($ch);
if (curl_errno($ch)) {
return curl_error($ch);
}
return $tmpInfo;
}
}
Copyright@支付开放平台,lnc,All rights reserved.