From 2aa74be1d7300af1638cb912ca1453c294b59ce3 Mon Sep 17 00:00:00 2001 From: cuikangyi Date: Fri, 11 Oct 2019 17:24:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=94=AF=E4=BB=98=E5=AE=9D?= =?UTF-8?q?=E5=85=AC=E9=92=A5=E8=AF=81=E4=B9=A6=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 + src/Gateways/Alipay.php | 5 ++ src/Gateways/Alipay/Support.php | 99 ++++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01e685456..cbbaf5a6b 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,9 @@ class PayController 'ali_public_key' => 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuWJKrQ6SWvS6niI+4vEVZiYfjkCfLQfoFI2nCp9ZLDS42QtiL4Ccyx8scgc3nhVwmVRte8f57TFvGhvJD0upT4O5O/lRxmTjechXAorirVdAODpOu0mFfQV9y/T9o9hHnU+VmO5spoVb3umqpq6D/Pt8p25Yk852/w01VTIczrXC4QlrbOEe3sr1E9auoC7rgYjjCO6lZUIDjX/oBmNXZxhRDrYx4Yf5X7y8FRBFvygIE2FgxV4Yw+SL3QAa2m5MLcbusJpxOml9YVQfP8iSurx41PvvXUMo49JG3BDVernaCYXQCoUJv9fJwbnfZd7J5YByC+5KM4sblJTq7bXZWQIDAQAB', // 加密方式: **RSA2** 'private_key' => 'MIIEpAIBAAKCAQEAs6+F2leOgOrvj9jTeDhb5q46GewOjqLBlGSs/bVL4Z3fMr3p+Q1Tux/6uogeVi/eHd84xvQdfpZ87A1SfoWnEGH5z15yorccxSOwWUI+q8gz51IWqjgZxhWKe31BxNZ+prnQpyeMBtE25fXp5nQZ/pftgePyUUvUZRcAUisswntobDQKbwx28VCXw5XB2A+lvYEvxmMv/QexYjwKK4M54j435TuC3UctZbnuynSPpOmCu45ZhEYXd4YMsGMdZE5/077ZU1aU7wx/gk07PiHImEOCDkzqsFo0Buc/knGcdOiUDvm2hn2y1XvwjyFOThsqCsQYi4JmwZdRa8kvOf57nwIDAQABAoIBAQCw5QCqln4VTrTvcW+msB1ReX57nJgsNfDLbV2dG8mLYQemBa9833DqDK6iynTLNq69y88ylose33o2TVtEccGp8Dqluv6yUAED14G6LexS43KtrXPgugAtsXE253ZDGUNwUggnN1i0MW2RcMqHdQ9ORDWvJUCeZj/AEafgPN8AyiLrZeL07jJz/uaRfAuNqkImCVIarKUX3HBCjl9TpuoMjcMhz/MsOmQ0agtCatO1eoH1sqv5Odvxb1i59c8Hvq/mGEXyRuoiDo05SE6IyXYXr84/Nf2xvVNHNQA6kTckj8shSi+HGM4mO1Y4Pbb7XcnxNkT0Inn6oJMSiy56P+CpAoGBAO1O+5FE1ZuVGuLb48cY+0lHCD+nhSBd66B5FrxgPYCkFOQWR7pWyfNDBlmO3SSooQ8TQXA25blrkDxzOAEGX57EPiipXr/hy5e+WNoukpy09rsO1TMsvC+v0FXLvZ+TIAkqfnYBgaT56ku7yZ8aFGMwdCPL7WJYAwUIcZX8wZ3dAoGBAMHWplAqhe4bfkGOEEpfs6VvEQxCqYMYVyR65K0rI1LiDZn6Ij8fdVtwMjGKFSZZTspmsqnbbuCE/VTyDzF4NpAxdm3cBtZACv1Lpu2Om+aTzhK2PI6WTDVTKAJBYegXaahBCqVbSxieR62IWtmOMjggTtAKWZ1P5LQcRwdkaB2rAoGAWnAPT318Kp7YcDx8whOzMGnxqtCc24jvk2iSUZgb2Dqv+3zCOTF6JUsV0Guxu5bISoZ8GdfSFKf5gBAo97sGFeuUBMsHYPkcLehM1FmLZk1Q+ljcx3P1A/ds3kWXLolTXCrlpvNMBSN5NwOKAyhdPK/qkvnUrfX8sJ5XK2H4J8ECgYAGIZ0HIiE0Y+g9eJnpUFelXvsCEUW9YNK4065SD/BBGedmPHRC3OLgbo8X5A9BNEf6vP7fwpIiRfKhcjqqzOuk6fueA/yvYD04v+Da2MzzoS8+hkcqF3T3pta4I4tORRdRfCUzD80zTSZlRc/h286Y2eTETd+By1onnFFe2X01mwKBgQDaxo4PBcLL2OyVT5DoXiIdTCJ8KNZL9+kV1aiBuOWxnRgkDjPngslzNa1bK+klGgJNYDbQqohKNn1HeFX3mYNfCUpuSnD2Yag53Dd/1DLO+NxzwvTu4D6DCUnMMMBVaF42ig31Bs0jI3JQZVqeeFzSET8fkoFopJf3G6UXlrIEAQ==', + // 使用公钥证书模式,请配置下面两个参数,同时修改ali_public_key为以.crt结尾的支付宝公钥证书路径,如(./cert/alipayCertPublicKey_RSA2.crt) + // 'app_cert_public_key' => './cert/appCertPublicKey.crt', //应用公钥证书路径 + // 'alipay_root_cert' => './cert/alipayRootCert.crt', //支付宝根证书路径 'log' => [ // optional 'file' => './logs/alipay.log', 'level' => 'info', // 建议生产环境等级调整为 info,开发环境为 debug diff --git a/src/Gateways/Alipay.php b/src/Gateways/Alipay.php index d87cca3ea..6f13e2f20 100644 --- a/src/Gateways/Alipay.php +++ b/src/Gateways/Alipay.php @@ -91,6 +91,11 @@ public function __construct(Config $config) 'biz_content' => '', 'app_auth_token' => $config->get('app_auth_token'), ]; + + if ($config->get('app_cert_public_key') && $config->get('alipay_root_cert')) { + $this->payload['app_cert_sn'] = Support::getCertSN($config->get('app_cert_public_key')); + $this->payload['alipay_root_cert_sn'] = Support::getRootCertSN($config->get('alipay_root_cert')); + } } /** diff --git a/src/Gateways/Alipay/Support.php b/src/Gateways/Alipay/Support.php index abf7dc524..edf7efe01 100644 --- a/src/Gateways/Alipay/Support.php +++ b/src/Gateways/Alipay/Support.php @@ -199,7 +199,9 @@ public static function verifySign(array $data, $sync = false, $sign = null): boo throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]'); } - if (Str::endsWith($publicKey, '.pem')) { + if (Str::endsWith($publicKey, '.crt')) { + $publicKey = file_get_contents($publicKey); + } elseif (Str::endsWith($publicKey, '.pem')) { $publicKey = openssl_pkey_get_public( Str::startsWith($publicKey, 'file://') ? $publicKey : 'file://'.$publicKey ); @@ -353,4 +355,99 @@ protected function setHttpOptions(): self return $this; } + + /** + * 生成应用证书SN + * + * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html + * + * @param $certPath + * @return string + * @throws /Exception + */ + public static function getCertSN($certPath): string + { + if (!is_file($certPath)) { + throw new \Exception('unknown certPath -- [getCertSN]'); + } + $x509data = file_get_contents($certPath); + if ($x509data === false) { + throw new \Exception('Alipay CertSN Error -- [getCertSN]'); + } + openssl_x509_read($x509data); + $certdata = openssl_x509_parse($x509data); + if (empty($certdata)) { + throw new \Exception('Alipay openssl_x509_parse Error -- [getCertSN]'); + } + $issuer_arr = []; + foreach ($certdata['issuer'] as $key => $val) { + $issuer_arr[] = $key . '=' . $val; + } + $issuer = implode(',', array_reverse($issuer_arr)); + Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]); + return md5($issuer . $certdata['serialNumber']); + } + + /** + * 0x转高精度数字 + * + * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html + * + * @param $hex + * @return int|string + */ + private static function bchexdec($hex) + { + $dec = 0; + $len = strlen($hex); + for ($i = 1; $i <= $len; $i++) { + $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); + } + return $dec; + } + + /** + * 生成支付宝根证书SN + * + * @author 大冰 https://sbing.vip/archives/2019-new-alipay-php-docking.html + * + * @param $certPath + * @return string + * @throws /Exception + */ + public static function getRootCertSN($certPath) + { + if (!is_file($certPath)) { + throw new \Exception('unknown certPath -- [getRootCertSN]'); + } + $x509data = file_get_contents($certPath); + if ($x509data === false) { + throw new \Exception('Alipay CertSN Error -- [getRootCertSN]'); + } + $kCertificateEnd = "-----END CERTIFICATE-----"; + $certStrList = explode($kCertificateEnd, $x509data); + $md5_arr = []; + foreach ($certStrList as $one) { + if (!empty(trim($one))) { + $_x509data = $one . $kCertificateEnd; + openssl_x509_read($_x509data); + $_certdata = openssl_x509_parse($_x509data); + if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) { + $issuer_arr = []; + foreach ($_certdata['issuer'] as $key => $val) { + $issuer_arr[] = $key . '=' . $val; + } + $_issuer = implode(',', array_reverse($issuer_arr)); + if (strpos($_certdata['serialNumber'], '0x') === 0) { + $serialNumber = self::bchexdec($_certdata['serialNumber']); + } else { + $serialNumber = $_certdata['serialNumber']; + } + $md5_arr[] = md5($_issuer . $serialNumber); + Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]); + } + } + } + return implode('_', $md5_arr); + } }