定位
签名
这里我使用node.js的crypto模块签名,先把JSON转为字符串'{"a":"123"}'
,却总是得到公私钥不匹配的结果。不得已下载了支付宝提供的签名工具,在这款“支付宝报文签名生成器”的软件左下角,发现这样一行字:
默认SHA1签名,编码:GBK,预签名串:a=123
看来是支付宝埋的坑,于是改为对“a=123”签名,终于匹配了,下面是签名的示例代码:
let crypto = require('crypto');
let fs = require('fs');
let str = 'a=123';
console.log(str);
let private_key = fs.readFileSync('rsa_private_key.pem');
let signer = crypto.createSign('RSA-SHA1');
signer.update(str);
let sign = signer.sign(private_key, 'base64');
console.log(sign);
请求参数
- 参数中没有中文,这种情况比较简单,只对sign进行encodeuricomponent即可。
- 参数中有中文,这时需要对含中文的参数进行编码。
综合考虑,其实以上两点可以一起处理,即不考虑有无中文,都对所有参数进行编码。得到sign的签名值后,对整个参数Ojbect使用querystring模块处理即可:
const qs = require('querystring');
let signedParams = qs.stringify(params);
验证签名
这一步注意两点:
- 检查支付宝公钥是否正确
一开始我直接用了Github某个demo中的alipay_public_key.pem
文件,结果总是验签失败,最后在提问,才知道这个密钥是错误的。在管理中心中可以查看支付宝公钥,我用的pem文件如下:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDI6d306Q8fIfCOaTXyiUeJHkr
IvYISRcc73s3vF1ZT7XN8RNPwJxo8pWaJMmvyTn9N4HQ632qJBVHf8sxHi/fEsra
prwCtzvzQETrNRwVxLO5jVmRGi60j8Ue1efIlzPXV9je9mkjzOmdssymZkh2QhUr
CmZYI/FCEa3/cNMW0QIDAQAB
-----END PUBLIC KEY-----
- 转义正斜杠
支付宝要求,如果字符串中包含http://
的正斜杠,需要先将正斜杠做转义。比如返回数据中的,验签时要包含这个转义字符。javascript中使用replace即可。验签代码如下:
let signArg = res.alipay_trade_precreate_response;
let str = JSON.stringify(signArg).replace(/\//g, '\\/');
let pubkey = fs.readFileSync('alipay_public_key.pem');
let sign = res.sign;
let verify = crypto.createVerify('RSA-SHA1').update(str);
let ret = verify.verify(pubkey, sign, 'base64');
console.log(ret);