bztang-admin/vendor/wechatpay/wechatpay-guzzle-middleware/src/Auth/CertificateVerifier.php

109 lines
3.5 KiB
PHP

<?php
/**
* CertificateVerifier
* PHP version 5
*
* @category Class
* @package WechatPay
* @author WeChat Pay Team
* @link https://pay.weixin.qq.com
*/
namespace WechatPay\GuzzleMiddleware\Auth;
use WechatPay\GuzzleMiddleware\Auth\Verifier;
/**
* CertificateVerifier
*
* @category Class
* @package WechatPay
* @author WeChat Pay Team
* @link https://pay.weixin.qq.com
*/
class CertificateVerifier implements Verifier
{
/**
* WechatPay Certificates Public Keys
*
* @var array (serialNo => publicKey)
*/
protected $publicKeys = [];
/**
* Constructor
*
* @param array of string|resource $certifcates WechatPay Certificates (string - PEM formatted \
* certificate, or resource - X.509 certificate resource returned by openssl_x509_read)
*/
public function __construct(array $certificates)
{
foreach ($certificates as $certificate) {
$serialNo = $this->parseSerialNo($certificate);
$this->publicKeys[$serialNo] = \openssl_get_publickey($certificate);
}
}
/**
* Verify signature of message
*
* @param string $serialNumber certificate serial number
* @param string $message message to verify
* @param string $signautre signature of message
*
* @return bool
*/
public function verify($serialNumber, $message, $signature)
{
$serialNumber = \strtoupper(\ltrim($serialNumber, '0')); // trim leading 0 and uppercase
if (!isset($this->publicKeys[$serialNumber])) {
return false;
}
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
}
$signature = \base64_decode($signature);
return \openssl_verify($message, $signature, $this->publicKeys[$serialNumber],
'sha256WithRSAEncryption');
}
/**
* Parse Serial Number from Certificate
*
* @param string|resource $certifcates WechatPay Certificates (string - PEM formatted \
* certificate, or resource - X.509 certificate resource returned by openssl_x509_read)
*
* @return string
*/
protected function parseSerialNo($certificate)
{
$info = \openssl_x509_parse($certificate);
if (!isset($info['serialNumber']) && !isset($info['serialNumberHex'])) {
throw new \InvalidArgumentException('证书格式错误');
}
$serialNo = '';
// PHP 7.0+ provides serialNumberHex field
if (isset($info['serialNumberHex'])) {
$serialNo = $info['serialNumberHex'];
} else {
// PHP use i2s_ASN1_INTEGER in openssl to convert serial number to string,
// i2s_ASN1_INTEGER may produce decimal or hexadecimal format,
// depending on the version of openssl and length of data.
if (\strtolower(\substr($info['serialNumber'], 0, 2)) == '0x') { // HEX format
$serialNo = \substr($info['serialNumber'], 2);
} else { // DEC format
$value = $info['serialNumber'];
$hexvalues = ['0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'];
while ($value != '0') {
$serialNo = $hexvalues[\bcmod($value, '16')].$serialNo;
$value = \bcdiv($value, '16', 0);
}
}
}
return \strtoupper($serialNo);
}
}