CURL使用SSL证书访问HTTPS

  1. 支付容易退款难
    1. 带有私钥的证书
    2. 二进制编码的证书
    3. Base64编码的证书
    4. PKCS#12 到 PEM 的转换
  2. 参考,希望对你也有帮助

支付容易退款难

在支付的交互过程中,安全绝对是需要考虑的重要因素之一。体现在对服务器交互数据的签名等环节,但有的时候为了能达到更高的安全级别,还需要用ssl证书,即web服务器有证书,浏览器客户端/请求端也需要安装证书来达到双向验证。

比如请求下面的财付通支付网关,用户向商户账户支付金额,仅仅是需要检查签名就行了

https://gw.tenpay.com/gateway/pay.htm

但是在退款这一步要请求财付通退款接口时,不仅要验证签名,还要双向验证SSL证书,一旦让自己出钱的时候就变得抠门谨慎了

https://mch.tenpay.com/refundapi/gateway/refund.xml

这个接口就需要安装在开通服务之后第三方给我们发送的安全证书了。

作为文件形式存在的证书一般有这几种格式:

带有私钥的证书

由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀名。

二进制编码的证书

证书中没有私钥,DER 编码二进制格式的证书文件,以cer作为证书文件后缀名。

Base64编码的证书

证书中没有私钥,BASE64 编码格式的证书文件,也是以cer作为证书文件后缀名。

由定义可以看出,只有pfx格式的数字证书是包含有私钥的,cer格式的数字证书里面只有公钥没有私钥。

在pfx证书的导入过程中有一项是“标志此密钥是可导出的。这将您在稍候备份或传输密钥”。一般是不选中的,如果选中,别人就有机会备份你的密钥了。如果是不选中,其实密钥也导入了,只是不能再次被导出。这就保证了密钥的安全。

如果导入过程中没有选中这一项,做证书备份时“导出私钥”这一项是灰色的,不能选。只能导出cer格式的公钥。如果导入时选中该项,则在导出时“导出私钥”这一项就是可选的。

上图,如果要导出私钥(pfx),是需要输入密码的,这个密码就是对私钥再次加密,这样就保证了私钥的安全,别人即使拿到了你的证书备份(pfx),不知道加密私钥的密码,也是无法导入证书的。相反,如果只是导入导出cer格式的证书,是不会提示你输入密码的。因为公钥一般来说是对外公开的,不用加密
由于php的curl只支持pem格式、der、eng格式,而之前生成的是p12的格式,所以需要转换一下

PKCS#12 到 PEM 的转换

openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem

验证

openssl pkcs12 -clcerts -nokeys -in cert.p12 -out cert.pem

curl请求部分代码

/**
* 使用证书访问退款接口
*
*/
private static function _postCert($url)
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, '2');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, '1');
curl_setopt($ch, CURLOPT_SSLCERT, WECHAT_CERT);
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$response = curl_exec($ch);
return $response;
}

参数释义:

CURLOPT_TIMEOUT:超时时间
CURLOPT_RETURNTRANSFER:是否要求返回数据
CURLOPT_SSL_VERIFYPEER:是否检测服务器的证书是否由正规浏览器认证过的授权CA颁发的
CURLOPT_SSL_VERIFYHOST:是否检测服务器的域名与证书上的是否一致
CURLOPT_SSLCERTTYPE:证书类型,"PEM" (default), "DER", and"ENG".
CURLOPT_SSLCERT:证书存放路径
CURLOPT_SSLCERTPASSWD:证书密码
CURLOPT_SSLKEYTYPE:私钥类型,"PEM" (default), "DER", and"ENG".
CURLOPT_SSLKEY:私钥存放路径

继续返回false 让输出错误显示:

Peer certificate cannot be authenticated with known CA certificates

对方的证书不能用已知的CA证书验证

其实和使用curl访问一个意思

➜ Certificate curl -v https://mch.tenpay.com
* Rebuilt URL to: https://mch.tenpay.com/
* Hostname was NOT found in DNS cache
* Trying 183.62.126.37...
* Connected to mch.tenpay.com (183.62.126.37) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS alert, Server hello (2):
* SSL certificate problem: self signed certificate in certificate chain
* Closing connection 0
curl: (60) SSL certificate problem: self signed certificate in certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.

最后的一个解决方案

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

返回

<?xml version="1.0" encoding="UTF-8"?>
<root>
<input_charset>UTF-8</input_charset>
<out_refund_no />
<out_trade_no />
<partner>1226251301</partner>
<reccv_user_name />
<recv_user_id />
<refund_channel />
<refund_fee />
<refund_id />
<refund_status>-1</refund_status>
<retcode>88222013</retcode>
<retmsg>[20921191]退款总金额超出交易金额[20150313174732-183]</retmsg>
<sign>010651C7B3906F2863B3B1D2FB63A571</sign>
<sign_key_index>1</sign_key_index>
<sign_type>MD5</sign_type>
<transaction_id />
</root>

参考,希望对你也有帮助

script>