问题描述:
对接易通银行,他们的服务开放平台是java开发,而我的是php,现在需要php版本的SDK demo 和java交互。对于cer文件和pfx文件根本不懂,不知道是做什么用的,以前也是用rsa加密,使用公钥私钥。而对方直接给的pfx文件。
解决办法:
pfx格式的证书,是商户证书
cer格式的证书,是平台证书
一、pfx格式的证书
pfx文件也可以转为pem格式的证书再操作;也可以使用openssl_pkcs12_read(),openssl_x509_parse()函数操作。
pfx里面包含两部分内容,一个是商户私钥,一个是商户公钥
二、cer格式的证书
在代码中可能用到的和cer证书有关的php函数openssl_pkey_get_public($publickey),openssl_verify($keyValueStr,$sign,$pkeyid,OPENSSL_ALGO_SHA256)等
cer里面包含1部分内容,就是平台公钥
三、这2个证书的意义
pfx证书,一般叫私钥证书(商户证书),在代码的执行流程中它到底起什么作业?回答是:加签
1、商户通过开放平台提供的参数,安装要求组装一个字符串比如:$keyValueStr='key1=value1&key2=value2&key3=value3....&';
2、使用$pfx = get_file_contents('pfx证书路径')读取私钥证书
3、openssl_pkcs12_read($pfx,$result,$ClientPwd); $ClientPwd是证书密钥,银行的开放平台会提供给你的。$result是返回值,返回的是一个数组
4、这个地方只要用到$result数组pkey元素的内容即:$result['pkey']。 $pkey = $result['pkey'];
5、$pkeyid = openssl_pkey_get_private($pkey); //真正的获取私钥,不过返回的数据类型为资源
6、使用获取到的私钥资源id,对$keyValueStr='key1=value1&key2=value2&key3=value3....&';字符串进行加签,当然加签是又对应好几种算法,我们这个平台使用的OPENSSL_ALGO_SHA256算法。
openssl_sign($keyValueStr,$signature,$pkeyid,OPENSSL_ALGO_SHA256); //$signature 为返回的签名串
7、对签名串进行base64_encode($signature);处理 //不是必须的,看平台的约定情况
8、把最终得到的字符串发送给开放平台服务端
完了服务端在做验签等待,这里不描述了。那是服务端的事情,咱们这里重点是说客户端sdk
按照商量的顺序严格执行,我们就会看到pfx证书在执行流程中的作用。那么你可能想知道为什么要用pfx中的私钥对字符串进行加签,对,就是为了安全,防止发送到服务端的数据被篡改。具体服务端如何验证数据是否安全的,那么咱们看看商户客户端又是如何验证服务端返回的数据安全的,其实是一个道理,看完就理解服务端验签过程了
cer证书,一般叫公钥证书(平台证书),在代码的执行流程中它到底起什么作业?回答是:验签
既然商户发给开放平台服务端的数据,服务端不信任要验签,那么商户客户端也是同样的道理,不信任开放平台返回的数据 ,也要对返回的数据验签。cer中的公钥就是这个作用。
openssl x509 -inform der -in xxx.cer -out xxx.pem
1、根据开放平台返回的数据,按照开放平台的约定对,把返回数据组合成$keyValueStr='key1=value1&key2=value2&key3=value3....&';
2、$public_key = file_get_contents($this->publicKeyPath);读取公钥证书内容
3、$pkeyid = openssl_pkey_get_public($publickey); //资源类型
4、$sign = base64_decode($sign); //不是必须的,看平台的约定情况
5、$verify = openssl_verify($keyValueStr,$sign,$pkeyid,OPENSSL_ALGO_SHA256); //$sign为开放平台服务端返回的签名串
6、只有验签成功,才能确认开放平台,服务端返回数据是安全的。
<?php namespace app\Http\Controllers\porxt; use App\Http\Controllers\Controller; /** * 微信小程序支付、APP支付;支付宝APP支付 */ class ApiController extends Controller { protected $keystorefile = "E:/phpstudy/WWW/gtzyc_api/certs/ton/106026.pfx";//商户证书 签名 私钥 protected $mercpublickey = "E:/phpstudy/WWW/zyc_api/certs/ton/ddkkx.pem";//对方公钥 验签 protected $keyPassWord = "";//证书密码 protected $serial_number = "";//商户证书序列号 protected $mer_id = "";//商户号 protected $mer_url="http://api.com/ext/ton/merurl";//返回用户前台展示的地址 protected $back_url="http://api.com/ext/ton/backurl";//异步通知的地址 protected $url="";//接口请求地址 // 异步通知的地址 public function backUrl(Request $req){ $param=$req->getContent(); Log::info("............异步通知:".$param); } // 返回用户前台展示的地址 public function merUrl(Request $req){ $param=$req->getContent(); Log::info("............返回用户前台展示的地址:".$param); } //请求 public function requestEtongGateWay(Request $req){ $datas=array( "version"=>"1.1",//版本号 "format" =>"JSON",//报文格式 "charset"=>"UTF-8",//编码格式 "app_id"=>"",//应用ID "tran_date_time"=>date("YmdHis"),//请求时间 "serial_number"=>$this->serial_number,//商户证书序列号 "content"=>array( "mer_order_num"=>$this->mer_id.date("Ymd").$this->suiji(9),//订单号 "tran_amt"=>10,//交易金额 "sys_trace_num"=>$this->suiji(32),//请求流水号 "mer_url"=>$this->mer_url,//返回用户前台展示的地址 "back_url"=>$this->back_url,//异步通知的地址 ) ); $sign=$this->SHA256withRSA($datas); Log::info("........统一扫码签名串:".$sign); $datas['sign_value']=$sign; $data=str_replace('\\', '',json_encode($datas)); $curl=$this->postcurl("QRanPay",$data); $yanqian=$this->checkrsa($curl['data']); } // public function postcurl($paytype='',$data){ $headerArray =array("Content-type:application/json;charset=UTF-8","Accept:application/json"); $url=$this->url.$paytype; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE); curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl,CURLOPT_HTTPHEADER,$headerArray); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($curl); $err = curl_error($curl); if ($err) { return ['result'=>2000,'msg'=>$err]; } if(empty($output)){ return ['result'=>2000,'msg'=>'返回数据为空']; } curl_close($curl); return ['result'=>1000,'msg'=>'返回数据为空','data'=>$output]; } /** * 获取待签名字符串 * @param array $datas 参数数组 * @return string */ public function getSignString($datas){ $pairs = array(); ksort($datas['biz_content']); reset($datas['biz_content']); foreach ($datas['biz_content'] as $k => $v) { $pairs[$k] = $v; } $pairs=json_encode($pairs); $pairs=str_replace('\\', '', $pairs); unset($datas['biz_content']); $pair = array(); $datas['biz_content']=$pairs; ksort($datas); reset($datas); foreach ($datas as $k => $v) { $pair[] = "$k=$v"; } return implode('&', $pair); } //验签排序 public function checkSignString($datas){ $pair = array(); ksort($datas); reset($datas); foreach ($datas as $k => $v) { if(!empty($v)){ $pair[] = "$k=$v"; } } return implode('&', $pair); } /** * 签名 * Author: Lin. */ public function SHA256withRSA($content) { $andstring=$this->getSignString($content);//排序拼接,待签名串 $filePath =$this->keystorefile; $passphrase=$this->keyPassWord; if(!file_exists($filePath)) { return false; } $certs = array(); $pkcs12 = file_get_contents($filePath); if (openssl_pkcs12_read($pkcs12, $certs,$passphrase)) { $privateKey = $certs['pkey']; $pKeyId = openssl_pkey_get_private($privateKey); $signature=''; openssl_sign($andstring, $signature, $pKeyId,OPENSSL_ALGO_SHA256); //签名 openssl_free_key($pKeyId); return base64_encode($signature); } else { Log::info(".....读取pfx错误:".openssl_error_string()); return false; } } //验签 public function checkrsa($signstring){ $jsontoarr=json_decode($signstring,true); $sign_value=$jsontoarr['sign_value']; unset($jsontoarr['sign_value']); $check=$this->checkSignString($jsontoarr); // 接入方公钥 $cerfile =$this->mercpublickey; $mercpublickey=file_get_contents($cerfile); //摘要及签名的算法 $algo=OPENSSL_ALGO_SHA256; //加载公钥 $publickey=openssl_pkey_get_public($mercpublickey); //验签 $verify=openssl_verify($check,base64_decode($sign_value),$publickey,$algo); return $verify; } public function suiji($num){ $a = range(0,9); for($i=1;$i < $num+1;$i++){ $b[] = array_rand($a); } return join("",$b); } } ?>