php银行开放平台接口:pfx格式和cer格式的证书

发布时间:2020-06-24作者:小灵龙点击:189

问题描述:
对接易通银行,他们的服务开放平台是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);

	    }
	}
	

	?>

 

标签: