appid = '38dc55300f454e28b6204226fbc3033d'; // $this->secret_key = '3dd02385af49beb9f2227f619e76bdc4'; // //测试替换url 测试码 // $this->check_url = 'https://wlc.nppa.gov.cn/test/authentication/check'; // $this->query_url = 'https://wlc.nppa.gov.cn/test/authentication/query'; // $this->loginout = 'https://wlc.nppa.gov.cn/test/collection/loginout'; // $this->isTest = true; $config = config('zxsm'); $this->appid = $config['app_id']; $this->secret_key = $config['secret_key']; $this->check_url = 'https://api.wlc.nppa.gov.cn/idcard/authentication/check'; $this->query_url = 'http://api2.wlc.nppa.gov.cn/idcard/authentication/query'; $this->loginout = 'http://api2.wlc.nppa.gov.cn/behavior/collection/loginout'; $this->isTest = false; //时间戳 $this->time = $this->getMillisecond(); } //入口在这里 //直接运行这里 //1.需要配置$this->check() 函数中的配置 query loginout 同上 public function index() { //testcase01-实名认证接口 //testcase02-实名认证接口 //testcase03-实名认证接口 $check_res = $this->check(); echo '
';
        var_dump($check_res);
        die();

        /*
        //testcase04-实名认证结果查询接口
        //testcase05-实名认证结果查询接口
        //testcase06-实名认证结果查询接口

        $query_res = $this->query();
        var_dump($query_res);die();

        //testcase07-游戏用户行为数据上报接口
        //testcase08-游戏用户行为数据上报接口
        $loginout_res = $this->loginout();
        var_dump($loginout_res);
        */
    }

    /**
     * [check 实名认证接口]
     * @Author   shyn0121@qq.com
     * @DateTime 2021-03-05
     * @route    [route]
     * @return   [type]     [description] ($url, $data, $headers, $is_post = true, $is_ssl = false)
     */
    public function check($bizid = '', $ai = '', $realname = '', $idcard = '')
    {
        $this->initConfig();
        if($this->isTest){
            $ai = '100000000000000001';
        }
        //测试数据替换数据 用预置数据测试 https://wlc.nppa.gov.cn/fcm_company/%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F%E9%98%B2%E6%B2%89%E8%BF%B7%E5%AE%9E%E5%90%8D%E8%AE%A4%E8%AF%81%E7%B3%BB%E7%BB%9F%E6%B5%8B%E8%AF%95%E7%B3%BB%E7%BB%9F%E8%AF%B4%E6%98%8E.pdf
        //下面的数据测出为失败 一定要用预置数据哦 query 同理 loginout 也要注意配置
        if (!trim($bizid) || !trim($ai) || !trim($realname) || !trim($idcard)) {
            $retData = array();
            $retData['errcode'] = 9999;
            $retData['errmsg'] = '验证参数不完整';
            return $retData;
        }
        if (trim($bizid)) {
            $this->bizid = trim($bizid);
        }
        if (trim($ai)) {
            $this->ai = trim($ai);
        }
        if (trim($realname)) {
            $this->name = trim($realname);
        }
        if (trim($idcard)) {
            $this->id_num = trim($idcard);
        }


        /*
        if (empty($this->ai) || empty($this->name) || empty($this->id_num)) {
            die("body请对配置参数赋值");
        }
        */
        //业务参数
        $body_params = [
            'ai' => $this->ai,
            'name' => $this->name,
            'idNum' => $this->id_num,
        ];
        log_message($body_params, 'log', LOG_PATH . 'authentication/');
        //string
        $this->body = $this->getBody($body_params);
        //	echo $this->body."----body-----
"; $this->sign = $this->getSign($this->body); $this->header = $this->makeHeader($this->getHeaders($this->sign)); $return_data = $this->getJson($this->check_url, $this->body, $this->header, true); $errmsg = $return_data['errmsg']; if ($return_data['errcode'] > 1000 & $return_data['errcode'] < 2000) { $errmsg = '系统异常,请联系开发者!'; curlDD("中宣实名认证接口异常:" . $return_data['errcode'] . ' - ' . $return_data['errmsg'], Env::get('dingtalk.warning_url')); } if ($return_data['errcode'] > 2000) { $errmsg = '实名认证业务异常!'; if ($return_data['errcode'] == 2001) { $errmsg = '身份证号校验不通过!'; } else if ($return_data['errcode'] == 2005) { $errmsg = '姓名合法性校验不通过!'; } else { curlDD("中宣实名认证(check)接口失败:" . $return_data['errcode'] . ' - ' . $return_data['errmsg'], Env::get('dingtalk.notic_url')); } } $return_data['errmsg'] = $errmsg; return $return_data; } public function checkV2($bizid = '', $ai = '', $realname = '', $idcard = '') { $this->initConfig(); if($this->isTest){ $ai = '100000000000000001'; } //下面的数据测出为失败 一定要用预置数据哦 query 同理 loginout 也要注意配置 if (!trim($bizid) || !trim($ai) || !trim($realname) || !trim($idcard)) { return ['code' => -110, 'data' => [], 'msg' => '验证参数不完整']; } if (trim($bizid)) { $this->bizid = trim($bizid); } if (trim($ai)) { $this->ai = trim($ai); } if (trim($realname)) { $this->name = trim($realname); } if (trim($idcard)) { $this->id_num = trim($idcard); } //业务参数 $body_params = [ 'ai' => $this->ai, 'name' => $this->name, 'idNum' => $this->id_num, ]; log_message($body_params, 'log', LOG_PATH . 'authentication/'); $this->body = $this->getBody($body_params); $this->sign = $this->getSign($this->body); $this->header = $this->makeHeader($this->getHeaders($this->sign)); $return_data = $this->getJson($this->check_url, $this->body, $this->header, true); /** * 返回成功示例: * *array(3) { * ["errcode"] => int(0) * ["errmsg"] => string(2) "OK" * ["data"] => array(1) { * ["result"] => array(2) { * ["status"] => int(0) * ["pi"] => string(38) "1hhi7599umwfpjhy9sdbk8qn3sids5jfw86ez2" * } * } * } */ if($return_data['errcode'] === 0){ return ['code' => 200, 'data' => $return_data['data']['result'], 'msg' => 'success']; } $errmsg = $return_data['errmsg']; if ($return_data['errcode'] > 1000 & $return_data['errcode'] < 2000) { $errmsg = '认证系统异常,请联系商务反馈!'; curlDD("中宣实名认证接口异常:" . $return_data['errcode'] . '; ' . $return_data['errmsg']."; ai={$this->ai}", Env::get('dingtalk.warning_url')); } if ($return_data['errcode'] > 2000) { if ($return_data['errcode'] == 2001) { $errmsg = '认证失败,身份证号校验不通过!'; } else if ($return_data['errcode'] == 2004) { $errmsg = '认证失败,认证提交频繁,请稍后!'; } else if ($return_data['errcode'] == 2005) { $errmsg = '认证失败,姓名合法性校验不通过!'; } else { $errmsg = '认证失败,实名认证业务异常!'; curlDD("中宣实名认证(check)接口失败:" . $return_data['errcode'] . '; ' . $return_data['errmsg']."; ai={$this->ai}", Env::get('dingtalk.notic_url')); } } return ['code' => -100, 'data' => [], 'msg' => $errmsg]; } /** * [query 实名认证结果查询接口] * @Author shyn0121@qq.com * @DateTime 2021-03-05 * @route [route] * @return [type] [description] */ public function query($bizid = '', $ai = '') { //需要用预置数据https://wlc.nppa.gov.cn/fcm_company/%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F%E9%98%B2%E6%B2%89%E8%BF%B7%E5%AE%9E%E5%90%8D%E8%AE%A4%E8%AF%81%E7%B3%BB%E7%BB%9F%E6%B5%8B%E8%AF%95%E7%B3%BB%E7%BB%9F%E8%AF%B4%E6%98%8E.pdf /* $this->ai = '300000000000000005'; //游戏内部对应的唯一标识建议32位 $this->bizid = "1101999999"; */ $this->initConfig(); if($this->isTest){ $ai = '100000000000000001'; } if (!trim($bizid) || !trim($ai)) { $retData = array(); $retData['errcode'] = 9999; $retData['errmsg'] = '查询参数不完整'; return $retData; } if (trim($bizid)) { $this->bizid = trim($bizid); } if (trim($ai)) { $this->ai = trim($ai); } /* if (empty($this->ai)) { die("body请对配置参数赋值"); } */ //业务参数 $body_params = [ 'ai' => $this->ai, ]; //string $this->sign = $this->getSign($body_params); $this->header = $this->makeHeader($this->getHeaders($this->sign)); $return_data = $this->getJson($this->query_url . '?ai=' . $this->ai, '', $this->header, false); return $return_data; } /** * [loginout 游戏用户行为数据上报接口] * @param $bizid * @param $sid 一个会话标识只能对应唯一的实名用户,一个实名用户可以拥有多个会话标识;同一用户单次游戏会话中,上下线动作必须使用同一会话标识上报备注:会话标识仅标识一次用户会话,生命周期仅为一次上线和与之匹配的一次下线,不会对生命周期之外的任何业务有任何影响 * @param $bt 游戏用户行为类 0:下线1:上线 * @param $ct 用户行为数据上报类型 0:已认证通过用户2:游客用户 * @param $di 游客模式设备标识,由游戏运营单位生成,游客用户下必填 * @param $pi 已通过实名认证用户的唯一标识,已认证通过用户必填 * @return array|int|mixed */ public function loginout($bizid = '', $sid = '', $bt = 1, $ct = 0, $di = '', $pi = '') { //$this->bizid = "1101999999"; if (!trim($bizid)) { $retData = array(); $retData['errcode'] = 9999; $retData['errmsg'] = '查询参数不完整'; return $retData; } if (trim($bizid)) { $this->bizid = trim($bizid); } $this->initConfig(); /* $cdata = [ //可行 游客 'no' => 1, 'si' => 'w7ligxjw355ftctm94yqt9dcew4zd723', 'bt' =>0, 'ot' => time(), 'ct' => 2, 'di'=>'uyiv6clpf7cu296pd4ppv11le820dhkw', 'pi' => '1fffbjzos82bs9cnyj1dna7d6d29zg4esnh99u', //可行 已认证 'no' => 1, 'si' => 'w7ligxjw355ftctm94yqt9dcew4zd723', 'bt' =>0, 'ot' => time(), 'ct' => 0, 'pi' => '1fffbjzos82bs9cnyj1dna7d6d29zg4esnh99u', ]; */ $cdata = array(); $cdata['no'] = 1; $cdata['si'] = md5($sid); $cdata['bt'] = $bt; $cdata['ot'] = time() - 10; if ($ct == 0) { $cdata['ct'] = $ct; $cdata['pi'] = trim($pi); } else { $cdata['ct'] = $ct; $cdata['di'] = trim($di); $cdata['pi'] = trim($pi); } // var_dump($cdata); //业务参数 $body_params = [ 'collections' => [$cdata], ]; //string $this->body = $this->getBody($body_params); $this->sign = $this->getSign($this->body); $this->header = $this->makeHeader($this->getHeaders($this->sign)); $return_data = $this->getJson($this->loginout, $this->body, $this->header, true); // var_dump($return_data); return $return_data; } /** * [接口调用签名] * @Author shyn0121@qq.com * @DateTime 2021-03-05 * @data [请求体 json] * @return [type] [description] */ public function getSign($rawBody) { $data = $this->getHeaders(); if (is_array($rawBody)) { $data += $rawBody; } ksort($data); $source = [$this->secret_key]; foreach ($data as $key => $value) { if ($key !== 'sign' && $key != 'Content-Type') { $source[] = "{$key}{$value}"; } } if (!is_array($rawBody)) { $source[] = $rawBody; } $presign = implode("", $source); return hash("sha256", $presign); } //报文体 public function getBody($data) { // $string = Aes::encrypt(json_encode($data), $this->secret_key); $string = $this->aesGcmEncrypt($data); return json_encode(['data' => $string]); } public function aesGcmEncrypt($string) { $cipher = strtolower('AES-128-GCM'); if (is_array($string)) $string = json_encode($string); //二进制key $skey = hex2bin($this->secret_key); //二进制iv $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher)); list($content, $tag) = AESGCM::encrypt($skey, $iv, $string); //如果环境是php7.1+,直接使用下面的方式 // $tag = NULL; // $content = openssl_encrypt($string, $cipher, $skey,OPENSSL_RAW_DATA,$iv,$tag); $str = bin2hex($iv) . bin2hex($content) . bin2hex($tag); return base64_encode(hex2bin($str)); } public function getHeaders($sign = null) { return [ 'Content-Type' => "application/json;charset=utf-8", // "Content-type:multipart/form-data", // "Content-type:application/x-www-form-urlencoded", "appId" => $this->appid, "bizId" => $this->bizid, "timestamps" => $this->time, "sign" => $sign, ]; } public function makeHeader($params) { $header = []; foreach ($params as $k => $v) { $header[] = $k . ': ' . $v; } return $header; } /** * [getJson description] * @Author shyn0121@qq.com * @DateTime 2021-03-08 * @route [route] * @param [string] $url [url] * @param string $post_body [请求数据] * @param array $headers [请求头] * @param boolean $is_post [请求方式 post:true get:false] * @param boolean $is_ssl [description] * @return [array] [返回值] */ public function getJson($url, $post_body = '', $headers = [], $is_post = true, $is_ssl = false) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $is_ssl); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, $is_post); curl_setopt($ch, CURLOPT_TIMEOUT, 5); //设置超时 curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //$headers array if ($post_body) { curl_setopt($ch, CURLOPT_POSTFIELDS, $post_body); //$post_body = json串 } $output = curl_exec($ch); $errorno = curl_errno($ch); if (!$output || $errorno) { return $errorno; } curl_close($ch); // log_message($url . $output, 'log', LOG_PATH . 'authentication/'); trace('auth_zxsm:'.$url .' - '. $output, 'Authentication@getJson:result'); return json_decode($output, true); } public function getMillisecond() { list($msec, $sec) = explode(' ', microtime()); return (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000); } } /** * Class Aes 对称加密 * version : (PHP 7.1+) */ class Aes { /** * php7.1++ * [encrypt 加密] * @Author shyn0121@qq.com * @DateTime 2021-03-05 * @route [route] * @param string $data [加密数据] json串 * @param string $key [key] * @param string $type [解密类型] * @param [type] $options [description] * @param string $tag [引用] * @return [type] [description] */ public static function encrypt($data, $key, $cipher = 'aes-128-gcm', $options = OPENSSL_RAW_DATA, $tag = null) { $encrypt_data = false; //必须将key 转成ascii $key = hex2bin($key); // $options = in_array($options, [OPENSSL_RAW_DATA, OPENSSL_ZERO_PADDING]) ? $options : OPENSSL_RAW_DATA; if (in_array($cipher, openssl_get_cipher_methods())) { $ivlen = openssl_cipher_iv_length($cipher); $iv = openssl_random_pseudo_bytes($ivlen); $encrypt_data = openssl_encrypt($data, $cipher, $key, $options, $iv, $tag); $encrypt_data = base64_encode($iv . $encrypt_data . $tag); } return $encrypt_data; //下面也可以 // $key = hex2bin($key); // $ivlen = openssl_cipher_iv_length($cipher); // $iv = openssl_random_pseudo_bytes($ivlen); // $encrypt = openssl_encrypt($data, $cipher, $key, $options, $iv, $tag); // $encrypt = bin2hex($iv) . bin2hex($encrypt) . bin2hex($tag); // return base64_encode(hex2bin($encrypt)); } /** * [decrypt 解密] * @Author shyn0121@qq.com * @DateTime 2021-03-05 * @route [route] * @param string $data [解密数据] * @param string $key [key] * @param string $type [解密类型] * @param [type] $options [description] * @param string $tag [引用] * @return [type] [json] */ public static function decrypt($data, $key, $cipher = 'aes-128-gcm', $options = OPENSSL_RAW_DATA, $tag = "") { $r = base64_decode($data); $ivlen = openssl_cipher_iv_length($cipher); $key = hex2bin($key); $iv = substr($r, 0, 12); $tag = substr($r, -16); $ciphertext = substr($r, $ivlen, -16); $original_plaintext = openssl_decrypt($ciphertext, $cipher, $key, $options, $iv, $tag); if ($original_plaintext == false) { $err = openssl_error_string(); return $err; } return $original_plaintext; } } class AESGCM { /** * @param string $K Key encryption key * @param string $IV Initialization vector * @param null|string $P Data to encrypt (null for authentication) * @param null|string $A Additional Authentication Data * @param int $tag_length Tag length * * @return array */ public static function encrypt($K, $IV, $P = null, $A = null, $tag_length = 128) { /* Assertion::string($K, 'The key encryption key must be a binary string.'); */ $key_length = mb_strlen($K, '8bit') * 8; /* Assertion::inArray($key_length, [128, 192, 256], 'Bad key encryption key length.'); Assertion::string($IV, 'The Initialization Vector must be a binary string.'); Assertion::nullOrString($P, 'The data to encrypt must be null or a binary string.'); Assertion::nullOrString($A, 'The Additional Authentication Data must be null or a binary string.'); Assertion::integer($tag_length, 'Invalid tag length. Supported values are: 128, 120, 112, 104 and 96.'); Assertion::inArray($tag_length, [128, 120, 112, 104, 96], 'Invalid tag length. Supported values are: 128, 120, 112, 104 and 96.'); */ if (version_compare(PHP_VERSION, '7.1.0RC5') >= 0 && null !== $P) { return self::encryptWithPHP71($K, $key_length, $IV, $P, $A, $tag_length); } elseif (class_exists('\Crypto\Cipher')) { return self::encryptWithCryptoExtension($K, $key_length, $IV, $P, $A, $tag_length); } return self::encryptWithPHP($K, $key_length, $IV, $P, $A, $tag_length); } /** * This method will append the tag at the end of the ciphertext. * * @param string $K Key encryption key * @param string $IV Initialization vector * @param null|string $P Data to encrypt (null for authentication) * @param null|string $A Additional Authentication Data * @param int $tag_length Tag length * * @return string */ public static function encryptAndAppendTag($K, $IV, $P = null, $A = null, $tag_length = 128) { return implode(self::encrypt($K, $IV, $P, $A, $tag_length)); } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param null|string $P Data to encrypt (null for authentication) * @param null|string $A Additional Authentication Data * @param int $tag_length Tag length * * @return array */ private static function encryptWithPHP71($K, $key_length, $IV, $P = null, $A = null, $tag_length = 128) { $mode = 'aes-' . ($key_length) . '-gcm'; $T = null; $C = openssl_encrypt($P, $mode, $K, OPENSSL_RAW_DATA, $IV, $T, $A, $tag_length / 8); // Assertion::true(false !== $C, 'Unable to encrypt the data.'); return [$C, $T]; } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param null|string $P Data to encrypt (null for authentication) * @param null|string $A Additional Authentication Data * @param int $tag_length Tag length * * @return array */ private static function encryptWithPHP($K, $key_length, $IV, $P = null, $A = null, $tag_length = 128) { list($J0, $v, $a_len_padding, $H) = self::common($K, $key_length, $IV, $A); $C = self::getGCTR($K, $key_length, self::getInc(32, $J0), $P); $u = self::calcVector($C); $c_len_padding = self::addPadding($C); $S = self::getHash($H, $A . str_pad('', $v / 8, "\0") . $C . str_pad('', $u / 8, "\0") . $a_len_padding . $c_len_padding); $T = self::getMSB($tag_length, self::getGCTR($K, $key_length, $J0, $S)); return [$C, $T]; } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param null|string $P Data to encrypt (null for authentication) * @param null|string $A Additional Authentication Data * @param int $tag_length Tag length * * @return array */ private static function encryptWithCryptoExtension($K, $key_length, $IV, $P = null, $A = null, $tag_length = 128) { $cipher = \Crypto\Cipher::aes(\Crypto\Cipher::MODE_GCM, $key_length); $cipher->setAAD($A); $cipher->setTagLength($tag_length / 8); $C = $cipher->encrypt($P, $K, $IV); $T = $cipher->getTag(); return [$C, $T]; } /** * @param string $K Key encryption key * @param string $IV Initialization vector * @param string|null $C Data to encrypt (null for authentication) * @param string|null $A Additional Authentication Data * @param string $T Tag * * @return string */ public static function decrypt($K, $IV, $C, $A, $T) { /* Assertion::string($K, 'The key encryption key must be a binary string.'); $key_length = mb_strlen($K, '8bit') * 8; Assertion::inArray($key_length, [128, 192, 256], 'Bad key encryption key length.'); Assertion::string($IV, 'The Initialization Vector must be a binary string.'); Assertion::nullOrString($C, 'The data to encrypt must be null or a binary string.'); Assertion::nullOrString($A, 'The Additional Authentication Data must be null or a binary string.'); $tag_length = self::getLength($T); Assertion::integer($tag_length, 'Invalid tag length. Supported values are: 128, 120, 112, 104 and 96.'); Assertion::inArray($tag_length, [128, 120, 112, 104, 96], 'Invalid tag length. Supported values are: 128, 120, 112, 104 and 96.'); */ if (version_compare(PHP_VERSION, '7.1.0RC5') >= 0 && null !== $C) { return self::decryptWithPHP71($K, $key_length, $IV, $C, $A, $T); } elseif (class_exists('\Crypto\Cipher')) { return self::decryptWithCryptoExtension($K, $key_length, $IV, $C, $A, $T, $tag_length); } return self::decryptWithPHP($K, $key_length, $IV, $C, $A, $T, $tag_length); } /** * This method should be used if the tag is appended at the end of the ciphertext. * It is used by some AES GCM implementations such as the Java one. * * @param string $K Key encryption key * @param string $IV Initialization vector * @param string|null $Ciphertext Data to encrypt (null for authentication) * @param string|null $A Additional Authentication Data * @param int $tag_length Tag length * * @return string * * @see self::encryptAndAppendTag */ public static function decryptWithAppendedTag($K, $IV, $Ciphertext = null, $A = null, $tag_length = 128) { $tag_length_in_bits = $tag_length / 8; $C = mb_substr($Ciphertext, 0, -$tag_length_in_bits, '8bit'); $T = mb_substr($Ciphertext, -$tag_length_in_bits, null, '8bit'); return self::decrypt($K, $IV, $C, $A, $T); } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param string|null $C Data to encrypt (null for authentication) * @param string|null $A Additional Authentication Data * @param string $T Tag * * @return string */ private static function decryptWithPHP71($K, $key_length, $IV, $C, $A, $T) { $mode = 'aes-' . ($key_length) . '-gcm'; $P = openssl_decrypt(null === $C ? '' : $C, $mode, $K, OPENSSL_RAW_DATA, $IV, $T, null === $A ? '' : $A); // Assertion::true(false !== $P, 'Unable to decrypt or to verify the tag.'); return $P; } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param string|null $C Data to encrypt (null for authentication) * @param string|null $A Additional Authentication Data * @param string $T Tag * @param int $tag_length Tag length * * @return string */ private static function decryptWithPHP($K, $key_length, $IV, $C, $A, $T, $tag_length = 128) { list($J0, $v, $a_len_padding, $H) = self::common($K, $key_length, $IV, $A); $P = self::getGCTR($K, $key_length, self::getInc(32, $J0), $C); $u = self::calcVector($C); $c_len_padding = self::addPadding($C); $S = self::getHash($H, $A . str_pad('', $v / 8, "\0") . $C . str_pad('', $u / 8, "\0") . $a_len_padding . $c_len_padding); $T1 = self::getMSB($tag_length, self::getGCTR($K, $key_length, $J0, $S)); // Assertion::eq($T1, $T, 'Unable to decrypt or to verify the tag.'); return $P; } /** * @param string $K Key encryption key * @param string $key_length Key length * @param string $IV Initialization vector * @param string|null $C Data to encrypt (null for authentication) * @param string|null $A Additional Authentication Data * @param string $T Tag * @param int $tag_length Tag length * * @return string */ private static function decryptWithCryptoExtension($K, $key_length, $IV, $C, $A, $T, $tag_length = 128) { $cipher = \Crypto\Cipher::aes(\Crypto\Cipher::MODE_GCM, $key_length); $cipher->setTag($T); $cipher->setAAD($A); $cipher->setTagLength($tag_length / 8); return $cipher->decrypt($C, $K, $IV); } /** * @param $K * @param $key_length * @param $IV * @param $A * * @return array */ private static function common($K, $key_length, $IV, $A) { $H = openssl_encrypt(str_repeat("\0", 16), 'aes-' . ($key_length), $K, OPENSSL_NO_PADDING | OPENSSL_RAW_DATA); //--- $iv_len = self::getLength($IV); if (96 === $iv_len) { $J0 = $IV . pack('H*', '00000001'); } else { $s = self::calcVector($IV); // Assertion::eq(($s + 64) % 8, 0, 'Unable to decrypt or to verify the tag.'); $packed_iv_len = pack('N', $iv_len); $iv_len_padding = str_pad($packed_iv_len, 8, "\0", STR_PAD_LEFT); $hash_X = $IV . str_pad('', ($s + 64) / 8, "\0") . $iv_len_padding; $J0 = self::getHash($H, $hash_X); } $v = self::calcVector($A); $a_len_padding = self::addPadding($A); return [$J0, $v, $a_len_padding, $H]; } /** * @param string $value * * @return int */ private static function calcVector($value) { return (128 * ceil(self::getLength($value) / 128)) - self::getLength($value); } /** * @param string $value * * @return string */ private static function addPadding($value) { return str_pad(pack('N', self::getLength($value)), 8, "\0", STR_PAD_LEFT); } /** * @param string $x * * @return int */ private static function getLength($x) { return mb_strlen($x, '8bit') * 8; } /** * @param int $num_bits * @param int $x * * @return string */ private static function getMSB($num_bits, $x) { $num_bytes = $num_bits / 8; return mb_substr($x, 0, $num_bytes, '8bit'); } /** * @param int $num_bits * @param int $x * * @return string */ private static function getLSB($num_bits, $x) { $num_bytes = ($num_bits / 8); return mb_substr($x, -$num_bytes, null, '8bit'); } /** * @param int $s_bits * @param int $x * * @return string */ private static function getInc($s_bits, $x) { $lsb = self::getLSB($s_bits, $x); $X = self::toUInt32Bits($lsb) + 1; $res = self::getMSB(self::getLength($x) - $s_bits, $x) . pack('N', $X); return $res; } /** * @param string $bin * * @return mixed */ private static function toUInt32Bits($bin) { list(, $h, $l) = unpack('n*', $bin); return $l + ($h * 0x010000); } /** * @param $X * @param $Y * * @return string */ private static function getProduct($X, $Y) { $R = pack('H*', 'E1') . str_pad('', 15, "\0"); $Z = str_pad('', 16, "\0"); $V = $Y; $parts = str_split($X, 4); $x = sprintf('%032b%032b%032b%032b', self::toUInt32Bits($parts[0]), self::toUInt32Bits($parts[1]), self::toUInt32Bits($parts[2]), self::toUInt32Bits($parts[3])); $lsb_mask = "\1"; for ($i = 0; $i < 128; $i++) { if ($x[$i]) { $Z = self::getBitXor($Z, $V); } $lsb_8 = mb_substr($V, -1, null, '8bit'); if (ord($lsb_8 & $lsb_mask)) { $V = self::getBitXor(self::shiftStringToRight($V), $R); } else { $V = self::shiftStringToRight($V); } } return $Z; } /** * @param string $input * * @return string */ private static function shiftStringToRight($input) { $width = 4; $parts = array_map('self::toUInt32Bits', str_split($input, $width)); $runs = count($parts); for ($i = $runs - 1; $i >= 0; $i--) { if ($i) { $lsb1 = $parts[$i - 1] & 0x00000001; if ($lsb1) { $parts[$i] = ($parts[$i] >> 1) | 0x80000000; $parts[$i] = pack('N', $parts[$i]); continue; } } $parts[$i] = ($parts[$i] >> 1) & 0x7FFFFFFF; $parts[$i] = pack('N', $parts[$i]); } $res = implode('', $parts); return $res; } /** * @param string $H * @param string $X * * @return mixed */ private static function getHash($H, $X) { $Y = []; $Y[0] = str_pad('', 16, "\0"); $num_blocks = (int)(mb_strlen($X, '8bit') / 16); for ($i = 1; $i <= $num_blocks; $i++) { $Y[$i] = self::getProduct(self::getBitXor($Y[$i - 1], mb_substr($X, ($i - 1) * 16, 16, '8bit')), $H); } return $Y[$num_blocks]; } /** * @param string $K * @param int $key_length * @param string $ICB * @param string $X * * @return string */ private static function getGCTR($K, $key_length, $ICB, $X) { if (empty($X)) { return ''; } $n = (int)ceil(self::getLength($X) / 128); $CB = []; $Y = []; $CB[1] = $ICB; for ($i = 2; $i <= $n; $i++) { $CB[$i] = self::getInc(32, $CB[$i - 1]); } $mode = 'aes-' . ($key_length) . '-ecb'; for ($i = 1; $i < $n; $i++) { $C = openssl_encrypt($CB[$i], $mode, $K, OPENSSL_NO_PADDING | OPENSSL_RAW_DATA); $Y[$i] = self::getBitXor(mb_substr($X, ($i - 1) * 16, 16, '8bit'), $C); } $Xn = mb_substr($X, ($n - 1) * 16, null, '8bit'); $C = openssl_encrypt($CB[$n], $mode, $K, OPENSSL_NO_PADDING | OPENSSL_RAW_DATA); $Y[$n] = self::getBitXor($Xn, self::getMSB(self::getLength($Xn), $C)); return implode('', $Y); } /** * @param string $o1 * @param string $o2 * * @return string */ private static function getBitXor($o1, $o2) { $xorWidth = PHP_INT_SIZE; $o1 = str_split($o1, $xorWidth); $o2 = str_split($o2, $xorWidth); $res = ''; $runs = count($o1); for ($i = 0; $i < $runs; $i++) { $res .= $o1[$i] ^ $o2[$i]; } return $res; } } ?>