微信支付HTTPS服务器证书验证

微信支付计划于2018-05-29日更换服务器证书,这个通知已经N次了,一直不想整,没办法,时间快到了,得处理了。今天抽空整了下,写篇 blog 记录下:

	首先看:
		微信支付HTTPS服务器证书验证指引
		https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_4

	我对服务器不是很了解,对证书这个东西也没了解过。打开上面的这个链接,仔细阅读,大概明白了意思。我的理解是:
		『
			微信支付使用HTTPS来保证通信安全,商户与微信支付服务器通信前,要在客户端的操作系统或者执行环境(如JRE等)中部署权威机构的根证书。 商户调用API的过程中,会用根证书来校验微信支付服务器及域名的真实性。
		』

		我们使用 curl 命令,来请求微信支付 API 时,需要通过 『 同颁发给微信HTTPS服务器的证书 的同一家权威机构』的根证书,来验证我们请求的是不是真实的微信支付服务器及域名!

		所以,我们只需要下载新的根证书,然后部署到我们服务器上。curl 请求的时候,使用该证书即可!

	根据指引,让我们来验证:
		1.调用微信支付沙箱环境的API接口验证
			微信支付新的服务器证书,部署在 '沙箱环境',所以,我们来请求沙箱环境即可。
			『
				这里,我给出我本次测试的代码
				特殊说明:
					我的系统使用的 easywechat,为了验证,我参照按照官方的 demo 里的方法,
					重新写了个方法,来请求验证
			』

			/*
				上方的相关 config 配置自己更换
			 */
			 $mch_id,
			            'nonce_str' => $nonce_str,
			        ];

			        // 签名步骤一:按字典序排序参数
			        ksort($params);
			        $params_string = '';
			        foreach ($params as $k => $v)
			        {
			            if($k != 'sign' && $v != '' && !is_array($v)){
			                $params_string .= $k . '=' . $v . '&';
			            }
			        }
			        $request_params = $params_string = trim($params_string, '&');
			        // 签名步骤二:在string后加入KEY
			        $params_string .= '&key=' . $key;
			        // 签名步骤三:MD5加密
			        $params_string = md5($params_string);
			        // 签名步骤四:所有字符转为大写
			        $sign = strtoupper($params_string);

			        //
			        /*
			            微信请求,参数都必须是:
			                POST 请求
			                xml 格式

			            本次天真地以为,直接拼接为字符串,发现一直报错!(Mark 下错误)
			         */
			        // 最终的请求地址(Mark 下错误)
			        // $request_url = $sandbox_url . '?' . $request_params . '&sign=' . $sign;

			        // 请求的 xml 参数
			        $request_xml = <<
			    {$mch_id}
			    {$nonce_str}
			    {$sign}
			
			EOF;

			        // 发送请求
			        $ch = curl_init();

			        // curl 配置
			        curl_setopt($ch, CURLOPT_TIMEOUT, 60);

			        // 最终的请求地址(Mark 下错误)
			        // curl_setopt($ch, CURLOPT_URL, $request_url);
			        curl_setopt($ch, CURLOPT_URL, $sandbox_url);
			        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
			        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
			        // 设置 header
			        curl_setopt($ch, CURLOPT_HEADER, FALSE);
			        // 要求结果为字符串且输出到屏幕上
			        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
			        if(false){
			            // 设置证书
			            // 使用证书:cert 与 key 分别属于两个.pem文件
			            curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
			            curl_setopt($ch,CURLOPT_SSLCERT, $cert_path);
			            curl_setopt($ch,CURLOPT_SSLKEYTYPE, 'PEM');
			            curl_setopt($ch,CURLOPT_SSLKEY, $key_path);
			        }
			        // post提交方式
			        curl_setopt($ch, CURLOPT_POST, TRUE);
			        curl_setopt($ch, CURLOPT_POSTFIELDS, $request_xml);

			        $result = curl_exec($ch);
			        // 返回结果
			        if($result){
			            curl_close($ch);

			            // 解析结果
			            libxml_disable_entity_loader(true);
			            $response = json_decode(json_encode(simplexml_load_string($result, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
			            dd($response);
			        } else {
			            $error = curl_errno($ch);
			            curl_close($ch);
			            dd($error);
			        }

			/*
				重要!!
				微信内部提示:
					『 注意: 需要用跟生产环境相同的操作系统、执行环境、开发语言及程序逻辑进行验证。使用curl命令行工具验证成功,并不代表你的系统支持了新的服务器证书。 』
			 */
			意思就是:
				1.即使你用我上面的方法验证成功了,也不一定百分百说明你的程序系统支持了 『新服务器证书』,还需要确保,你的服务器环境、代码环境,都必须和你线上的环境一模一样。
				所以,我们其实可以直接在服务器上,使用我上面的方法,来访问下即可。(不用在你项目中的测试环境上访问)
				2.即使在线上服务器上,访问可以了,我的程序的写法,还需要和你目前系统的微信 curl 请求的写法,基本吻合,不要出现一些 curl 配置不同,可能也不行(概率很小,我的系统用的 easywechat,应该也没事)

		2.绑定HOST,请求已部署新证书的微信支付API服务器
			vim /etc/hosts,添加
				#113.96.240.139 		api.mch.weixin.qq.com
				#157.255.180.139        api.mch.weixin.qq.com
				#121.51.30.139  		api.mch.weixin.qq.com

			(一般没有问题吧)

	修正指引,引用 『微信支付指引』 里内容:
		『
			大部分情况下,验证失败都是由于以下两种情况导致的:

			情况一:程序中指定了根证书,但是指定的根证书中仅包含了微信支付老的根证书。

			情况二:服务器上没有新的根证书。


			可通过以下两个方案修正:

			方案一:删除掉指定根证书的。当不指定根证书时,程序默认会使用系统自带的根证书。绝大部分系统内置微信支付的根证书,所以删除掉指定的根证书,不会影响到你的现有业务。

			方案二:更新根证书。往truststore或者根证书信任列表中追加新的根证书

			 

			推荐使用“方案一”来修正,该方案对服务器证书的兼容性更好。原因是:微信支付新的根证书分别在 2025年5月13日、2031年11月10到期。到期后,同样地要更换根证书。使用方案一修复的话,你的系统中很可能已内置了后续微信支付更换时使用的根证书,从而不受影响。如果使用方案二进行修复的话,在2025年5月13日和2031年11月10日,仍可能需安装新的根证书。
		』

		再解读下:
			我们在 curl 时,不要设置 curl 的选项,指定一个 '根证书',例如:
				CURLOPT_CAINFO
				CURLOPT_CAPATH
				CURLOPT_SSL_VERIFYPEER
			这2个配置(除非你的系统,使用了多个不同的 CA 证书,一般应该不会有!),不指定根证书时,程序会默认使用系统的根证书。

	PHP 常见错误,引用 『微信支付指引』 里内容:
		『
			cURL error 60: SSL certificate: unable to get local issuer certificate.

			CURLE_SSL_CACERT (60)  peer certificate cannot be authenticated with known CA certificates.

			可能原因: 

			使用libcurl时设置了 CURLOPT_CAINFO。 可以在代码中搜索关键字“CURLOPT_CAINFO”来确认。 

			解决方法:

			方法一、删除掉类似  curl_setopt(pCurl, CURLOPT_CAINFO,  "./rootca.pem");   的代码

			方法二、更新rootca.pem。用libcurl官网最新的https://curl.haxx.se/ca/cacert.pem 替换即可 
		』

参考文章:
	微信支付 - 微信支付HTTPS服务器证书验证指引
		https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_4
	微信支付 - 协议规则
		https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=4_1
	微信支付 - 安全规范(签名算法)
		https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=4_3
	微信支付 - 签名校验工具
		https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=20_1	

	其他:
		https://www.easywechat.com/docs/master/zh-CN/troubleshooting
		https://blog.csdn.net/mazicwong/article/details/54946952
		https://blog.csdn.net/ecba1988/article/details/39204279

测试时,写验证代码时,注意:
	1.微信要求的是:POST 提交,数据格式,提交和返回数据都为XML格式,根节点名为xml。
		我拼接了下 get 和 post 的数据格式,死活不行!
		(因为,之前一直使用的是 easywechat,或者是官方的 demo 代码,自己没写过,这次踩了一遍才出现问题)

	2.对于程序中出现 'cURL error 60: SSL certificate: unable to get local issuer certificate.' 的错误:
		1>上面的解决方法是一种

		2>如果不需要证书验证,我们完全可以设置,不进行证书认证:
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => false,

你可能感兴趣的:(php,微信)