微信 支付即服务 服务人员注册 和 AEAD_AES_256_GCM 的使用 (sodium_crypto_aead_aes256gcm_decrypt 或者 sodium_crypto_aead_aes256gcm_is_available() )无法使用 的解决方案

sodium_crypto_aead_aes256gcm_decrypt 无法使用需要安装 sodium 

其实在 >=php7.2 以上的版本都自带了  这个模块 不用重新安装只需 将 

extension=sodium.so   前的 ;分号去掉保存  就可以了

 

如果您使用的是 windows  phpstudy  只需切换到 php >=7.2  勾选模块后重启即可 

 

 

 

 

直接上代码

 

<?php

class Test
{
    public $mch_id;
    public $api_serial_no;
    public $serial_no_obj;
    const KEY_LENGTH_BYTE = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;
    public $aesKey;

    public function __construct()
    {
        $this->mch_id = '16xxxxxxx6'; //商户号
        $this->api_serial_no = '321xxxxxxxxxx347'; //证书的序号
        $this->aesKey = 'zih3xxxxxxxxxxxxxxxxxxxxxnae'; //密钥
    }

    /**
     * 获取平台证书内容
     */
    public function get_Certificates($type)
    {
        if ($this->serial_no_obj) {
            $serial_no_obj = $this->serial_no_obj;
        } else {
            $sign = $this->get_Sign("https://api.mch.weixin.qq.com/v3/certificates", "GET", "", $this->get_Privatekey(), $this->mch_id, $this->api_serial_no);//$http_method要大写
            $header[] = 'User-Agent: */*';
            $header[] = 'Accept: application/json';
            $header[] = 'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $sign;
            $back = $this->http_Request("https://api.mch.weixin.qq.com/v3/certificates", $header);
            $serial_no_obj = json_decode($back);
            $this->serial_no_obj = $serial_no_obj;
        }
        if ($type == 1) {
            $serial_no_new = $serial_no_obj->data[0]->serial_no;
        } else if ($type == 2) {
            $serial_no_new = $serial_no_obj->data[0]->encrypt_certificate;
        } else {
            return $serial_no_obj;
        }
        return $serial_no_new;
    }

    /**
     * 获取sign
     * @param $url
     * @param $http_method [POST GET 必读大写]
     * @param $body [请求报文主体(必须进行json编码)]
     * @param $mch_private_key [商户私钥]
     * @param $merchant_id [商户号]
     * @param $serial_no [证书编号]
     * @return string
     */
    private function get_Sign($url, $http_method, $body, $mch_private_key, $merchant_id, $serial_no)
    {
        $timestamp = time();//时间戳
        $nonce = $timestamp . rand(10000, 99999);//随机字符串
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message =
            $http_method . "\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);
        return $token;
    }

    /**
     * 读取商户私钥
     * @return false|resource
     */
    public function get_Privatekey()
    {
        $private_key_file = 'apiclient_key.pem';//私钥文件路径 如linux服务器秘钥地址地址:/www/wwwroot/...../apiclient_key.pem"
        $mch_private_key = openssl_get_privatekey(file_get_contents($private_key_file));//获取私钥
        return $mch_private_key;
    }

    /**
     * 数据请求
     * @param $url
     * @param array $header 获取头部
     * @param string $post_data POST数据,不填写默认以GET方式请求
     * @return bool|string
     */
    public function http_Request($url, $header = array(), $post_data = "")
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 2);
        if ($post_data != "") {
            curl_setopt($ch, CURLOPT_POST, TRUE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); //设置post提交数据
        }
        //判断当前是不是有post数据的发
        $output = curl_exec($ch);
        if ($output === FALSE) {
            $output = "curl 错误信息: " . curl_error($ch);
        }
        curl_close($ch);
        return $output;
    }


    /**
     *敏感信息加密
     **/
    public function getEncrypt($str)
    {
        //返回的是加密后的平台证书,需要对证书进行解密
        $public_key_all = $this->get_Certificates(2);
        //解密证书,获取到平台证书得公钥
        $public_key = $this->decryptToString($public_key_all->associated_data, $public_key_all->nonce, $public_key_all->ciphertext);
        $encrypted = '';
        //$sign是加密后的字符串
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            $sign = '';
        }
        return $sign;
    }

    /**
     * Decrypt AEAD_AES_256_GCM ciphertext
     *
     * @param string $associatedData AES GCM additional authentication data
     * @param string $nonceStr AES GCM nonce
     * @param string $ciphertext AES GCM cipher text
     *
     * @return string|bool      Decrypted string on success or FALSE on failure
     */
    public function decryptToString($associatedData, $nonceStr, $ciphertext)
    {
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
            return false;
        }

        // ext-sodium (default installed on >= PHP 7.2)
        if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
            \sodium_crypto_aead_aes256gcm_is_available()) {
            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        }

        // ext-libsodium (need install libsodium-php 1.x via pecl)
        if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
            \Sodium\crypto_aead_aes256gcm_is_available()) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
        }

        // openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);

            return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,
                $authTag, $associatedData);
        }

        throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }

    //生成v3 Authorization
    public function createAuthorization($url, $body, $method = 'GET')
    {
        if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
            throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
        }
        $url_parts = parse_url($url);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));

        //API证书私钥地址
        $mch_private_key = $this->get_Privatekey();
        //当前时间戳
        $timestamp = time();
        //随机字符串
        $nonce = $timestamp . rand(10000, 99999);;
        //POST请求时 需要 转JSON字符串
        $message = "{$method}\n" .
            $canonical_url . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
        //生成签名
        openssl_sign($message, $raw_sign, openssl_get_privatekey($mch_private_key), 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        //生成签名信息
        $token = sprintf('mchid="%s",serial_no="%s",nonce_str="%s",timestamp="%d",signature="%s"', $this->mch_id, $this->api_serial_no, $nonce, $timestamp, $sign);
        $header = [
            'Content-Type: application/json',
            'Accept: application/json',
            'User-Agent: */*',
            'Authorization: WECHATPAY2-SHA256-RSA2048' . ' ' . $token,
            'Wechatpay-Serial: ' . $this->get_Certificates(1)
        ];
        return $header;
    }

    /**
     * 服务人员注册
     * @return array
     */
    function addWaiter()
    {
        //组合post信息,并对敏感信息加密
        $register = array(
            "corpid" => 'wwxxxxxxxxxxxxxx72',
            "store_id" => 100000000, //必须int类型
            "name" => $this->getEncrypt('小明明'),
            "mobile" => $this->getEncrypt('15xxxxxxxxx1'),
            "qr_code" => 'https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=xxxxxxxx1',
            "avatar" => "https://wework.qpic.cn/wwhead/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2qNxnJiboNbZHp7hU/0",
            "userid" => '15xxxxxxxxx1'
        );
        $registerJson = json_encode($register, JSON_UNESCAPED_UNICODE);
        //接口API
        $url = 'https://api.mch.weixin.qq.com/v3/smartguide/guides';
        //获取请求头(签名)
        $hearder = $this->createAuthorization($url, $registerJson, 'POST');
        $returnData = $this->http_Request($url, $hearder, $registerJson);
        //返回结果 {"guide_id" : "LLA3WJ6DSZUfiaZDS79FH5Wm5m4X69TBic"}
        exit($returnData);
    }
}

$aa = new Test();
echo $aa->addWaiter();

 

posted @ 2020-11-04 15:07  撑一支船蒿  阅读(1800)  评论(0编辑  收藏  举报