RSA签名规则实现:
基本请求规则
- client商户发起请求时, 需要对请求参数生成签名。
- service商户接收异步通知时,需要验证参数中的签名。
- RSA方式签名 --> 商户需要创建一对RSA秘钥,将公钥传给平台(service),私钥自行留存用于生成签名。 同时平台也会提供一个公钥给商户,用于验证签名。
签名算法
- Setp.1 确定待签名参数
在请求参数列表中,除去sign参数外,其他需要使用到的参数皆是要签名的参数。
在通知返回参数列表中,除去sign参数外,凡是通知返回回来的参数皆是要签名的参数。
- Setp.2 对参数进行排序
对于待签名的所有参数,需要根据参数名首字符字典顺序(ascii值大小)排序,若遇到相同首字符,则判断第二个字符,以此类推。
- Setp.3 生成待签名字符串
将排序后的待签名参数以“&“符号拼接, 形如:“参数名1=参数值1&参数名2=参数值2&….&参数名N=参数值N”。
- Setp.4 生成签名/验证签名
生成签名:把待签名字符串与商户的私钥一同放入RSA的签名函数中进行签名运算,从而得到签名结果字符串(sign值)。
验证签名:把待签名字符串、平台提供的公钥、通知返回参数中的参数sign值三者一同放入RSA的签名函数中进行签名运算,来判断签名是否验证通过。
- Setp.5 生成签名php用例
$pubKey = '-----BEGIN PUBLIC KEY-----
XXXXXXXXXXXXXXXXQEBAQUAA4GNADCBiQKBgQCD9glEeniobNraIF3MsFPQQ4h0
4Yu7e4g60MjTcJzgj1Dgfbap0a4r/Fg9b6ltsUhuJFkCsWoMinNzQ+9iSlb7nHo9
yRYNKBUaFocaj/pAjPCUmRzo5ieszhjSInR8/aMNqNIy7KLlkiogh1Bc6/m5S6IA
aXbUyiIooas3XXXXXXXXXXX
-----END PUBLIC KEY-----';
$priKey ='-----BEGIN RSA PRIVATE KEY-----
XXXXXXXXXXXXXXXXXXXXXXXbNraIF3MsFPQQ4h04Yu7e4g60MjTcJzgj1Dgfbap
0a4r/Fg9b6ltsUhuJFkCsWoMinNzQ+9iSlb7nHo9yRYNKBUaFocaj/pAjPCUmRzo
5ieszhjSInR8/aMNqNIy7KLlkiogh1Bc6/m5S6IAaXbUyiIooas367NwCQIDAQAB
AoGAVtkKaGMiHdbmgkBi+1/hCWQo3qPCTvWSRwM56M5osPIhjF2wZjt1ocVGXcir
fWH8BWEuo7MX1IEaUnYXilzauXPQHkH6cfMjF3uTLq+RAWWPyevTduqOm7jHM08l
XzfH74q2qtMCMtWQcElhHPlfRFySgDmX9g4V6OLXxH3Dx8ECQQCywdfXb38tg0Il
9b6rkP92G7rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXVytigEJZOYpZ9W
K0wzQgC9AkEAvPuYzwIZwcBKDBd1eALghN1B6ZKdIQ+vCMpezMormuOcR9Ls+JtR
RapOxsmqWUc5jJOkeTYY0kLgFntj44H/PQJALaEvkI2kgCCmTPxK3I6ZtzNi6nvL
kkmIPry9iKCIFqL/2u/QR/TiYC4RG6PtMBzkC8EVu+E/qNJ2Ns+8lsT9XQJAUmnW
BdCAswHMSOGiFKgMiWSD7fxGEbjpOAgot14jDfTcnmXZecEh/CN9Hyuq4iwxaKCq
RUHWKTuIxcUhloGGcQJAZ9DzAb/N2wAVATm8fX5e3gps3w+31mOVoemtBJA86QVe
gd+5SE3mDQP9DPOp0WaXXXXXXayK/elsg2/zl9JeOoQ==
-----END RSA PRIVATE KEY-----';
$params = [
"merchant_id"=>"1",
"uid"=>"2122334455",
"out_trade_id"=>"13423423423",
"amount"=>"88",
"subject"=>"活动红包"
];
//获取预处理字符串
$signString = getSignString($params);
//预处理字符串为
//amount=88&merchant_id=1&out_trade_id=13423423423&subject=活动红包&uid=2122334455
//获取签名
$sign = getSign($signString,$priKey);
//生成的签名为
//Najkbz6vZx1+HTpsmyeeawAUCbfpY2o2k/TAuhnZncf1KJ+DjfHK8q3+3Bo2pYYush98lsxLJ4Mv+C5qOZEYzdNbC/EIxfuR/Gx46Nrh8KK999NmSOpT0qaBfsVE7VIEPHQLFwieI7SWU/sOeBiEubXCyZUffSxKJLLnp1mKq9k=
//验证签名
$res = checkSign($pubKey,$sign,$signString);
var_dump($res);
//结果为 true
/**
* 生成签名
* @param string $signString 待签名字符串
* @param [type] $priKey 私钥
* @return string base64结果值
*/
function getSign($signString,$priKey){
$privKeyId = openssl_pkey_get_private($priKey);
$signature = '';
openssl_sign($signString, $signature, $privKeyId);
openssl_free_key($privKeyId);
return base64_encode($signature);
}
/**
* 校验签名
* @param string $pubKey 公钥
* @param string $sign 签名
* @param string $toSign 待签名字符串
* @return bool
*/
function checkSign($pubKey,$sign,$toSign){
$publicKeyId = openssl_pkey_get_public($pubKey);
$result = openssl_verify($toSign, base64_decode($sign), $publicKeyId);
openssl_free_key($publicKeyId);
return $result === 1 ? true : false;
}
/**
* 获取待签名字符串
* @param array $params 参数数组
* @return string
*/
function getSignString($params){
unset($params['sign']);
ksort($params);
reset($params);
$pairs = array();
foreach ($params as $k => $v) {
if(!empty($v)){
$pairs[] = "$k=$v";
}
}
return implode('&', $pairs);
}
注意
- 值为空字符串的参数无需参与签名。
- 根据HTTP协议要求,传递参数的值中如果存在特殊字符(如:&、@、+等),那么该值需要做URL Encoding,这样请求接收方才能接收到正确的参数值。这种情况下,待签名数据应该是原生值而不是encoding之后的值。例如:调用某接口需要对请求参数email进行数字签名,那么待签名数据应该是[email protected],而不是email=leyangjun%40meituan.com。
- Java签名需特殊处理
1.生成PKCS1的秘钥对 私钥和公钥
2.将PKCS1的秘钥转换成PKCS8的秘钥
3.PKCS8的秘钥供java使用
4.PKCS1的公钥供php使用
秘钥对生成方式
- 生成私钥:openssl genrsa -out rsa_private_key.pem 1024
- 生成公钥:openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
- 私钥格式转换:openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
赞成为第一个赞同者