| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024 |
- <?php
- //aes-128/gcm php_version:php7.1++
- //yii2
- //注意事项
- use think\Env;
- class Authentication
- {
- protected $appid, $secret_key, $bizid;
- protected $check_url, $query_url, $loginout;
- //待加密数据
- protected $ai, $name, $id_num;
- protected $time;
- protected $body;
- protected $header;
- protected $sign;
- protected $isTest;
- /**
- * [initConfig 初始化配置]
- * @Author shyn0121@qq.com
- * @DateTime 2021-03-05
- * @route [route]
- * @return [type] [description]
- */
- public function initConfig()
- {
- //测试 配置
- // $this->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 '<pre>';
- 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-----<br>";
- $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;
- }
- }
- ?>
|