543 lines
14 KiB
PHP
543 lines
14 KiB
PHP
![]() |
<?php
|
|||
|
|
|||
|
include './SecurityUtil.php';
|
|||
|
include './SecretGetRequest.php';
|
|||
|
include './TopSdkFeedbackUploadRequest.php';
|
|||
|
include './iCache.php';
|
|||
|
include '../../TopSdk.php';
|
|||
|
|
|||
|
class SecurityClient
|
|||
|
{
|
|||
|
private $topClient ;
|
|||
|
private $randomNum ;
|
|||
|
private $securityUtil;
|
|||
|
private $cacheClient = null;
|
|||
|
|
|||
|
function __construct($client, $random)
|
|||
|
{
|
|||
|
|
|||
|
define('APP_SECRET_TYPE','2');
|
|||
|
define('APP_USER_SECRET_TYPE','3');
|
|||
|
|
|||
|
$this->topClient = $client;
|
|||
|
$this->randomNum = $random;
|
|||
|
$this->securityUtil = new SecurityUtil();
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 设置缓存处理器
|
|||
|
*/
|
|||
|
function setCacheClient($cache)
|
|||
|
{
|
|||
|
$this->cacheClient = $cache;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 密文检索,在秘钥升级场景下兼容查询
|
|||
|
*
|
|||
|
* @see #search(String, String, String, Long)
|
|||
|
* @return
|
|||
|
*/
|
|||
|
function searchPrevious($data,$type,$session = null)
|
|||
|
{
|
|||
|
return $this->searchInner($data,$type,$session,-1);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 密文检索(每个用户单独分配秘钥)
|
|||
|
*
|
|||
|
* @see #search(String, String, String, Long)
|
|||
|
* @return
|
|||
|
*/
|
|||
|
function search($data,$type,$session = null)
|
|||
|
{
|
|||
|
return $this->searchInner($data,$type,$session,null);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 密文检索。 手机号码格式:$base64(H-MAC(phone后4位))$ simple格式:base64(H-MAC(滑窗))
|
|||
|
*
|
|||
|
* @param data
|
|||
|
* 明文数据
|
|||
|
* @param type
|
|||
|
* 加密字段类型(例如:simple\phone)
|
|||
|
* @param session
|
|||
|
* 用户身份,用户级加密必填
|
|||
|
* @param version
|
|||
|
* 秘钥历史版本
|
|||
|
* @return
|
|||
|
*/
|
|||
|
function searchInner($data, $type, $session,$version)
|
|||
|
{
|
|||
|
if(empty($data) || empty($type)){
|
|||
|
return $data;
|
|||
|
}
|
|||
|
|
|||
|
$secretContext = null;
|
|||
|
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,$version);
|
|||
|
$this->incrCounter(3,$type,$secretContext,true);
|
|||
|
|
|||
|
if(empty($secretContext) || empty($secretContext->secret)) {
|
|||
|
return $data;
|
|||
|
}
|
|||
|
|
|||
|
return $this->securityUtil->search($data, $type,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* 单条数据解密,使用appkey级别公钥
|
|||
|
* 非加密数据直接返回原文
|
|||
|
*/
|
|||
|
function decryptPublic($data,$type)
|
|||
|
{
|
|||
|
return $this->decrypt($data,$type,null);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 单条数据解密
|
|||
|
* 非加密数据直接返回原文
|
|||
|
*/
|
|||
|
function decrypt($data,$type,$session)
|
|||
|
{
|
|||
|
if(empty($data) || empty($type)){
|
|||
|
return $data;
|
|||
|
}
|
|||
|
$secretData = $this->securityUtil->getSecretDataByType($data,$type);
|
|||
|
if(empty($secretData)){
|
|||
|
return $data;
|
|||
|
}
|
|||
|
|
|||
|
if($this->securityUtil->isPublicData($data,$type)){
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
|||
|
} else {
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,$secretData->secretVersion);
|
|||
|
}
|
|||
|
$this->incrCounter(2,$type,$secretContext,true);
|
|||
|
|
|||
|
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 多条数据解密,使用appkey级别公钥
|
|||
|
* 非加密数据直接返回原文
|
|||
|
*/
|
|||
|
function decryptBatchPublic($array,$type)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
$result = array();
|
|||
|
foreach ($array as $value) {
|
|||
|
$secretData = $this->securityUtil->getSecretDataByType($value,$type);
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
|||
|
|
|||
|
if(empty($secretData)){
|
|||
|
$result[$value] = $value;
|
|||
|
}else{
|
|||
|
$result[$value] = $this->securityUtil->decrypt($value,$type,$secretContext);
|
|||
|
$this->incrCounter(2,$type,$secretContext,true);
|
|||
|
}
|
|||
|
|
|||
|
$this->flushCounter($secretContext);
|
|||
|
}
|
|||
|
|
|||
|
return $result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 多条数据解密,必须是同一个type和用户,返回结果是 KV结果
|
|||
|
* 非加密数据直接返回原文
|
|||
|
*/
|
|||
|
function decryptBatch($array,$type,$session)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
$result = array();
|
|||
|
foreach ($array as $value) {
|
|||
|
$secretData = $this->securityUtil->getSecretDataByType($value,$type);
|
|||
|
if(empty($secretData)){
|
|||
|
$result[$value] = $value;
|
|||
|
} else if($this->securityUtil->isPublicData($value,$type)){
|
|||
|
$appContext = $this->callSecretApiWithCache(null,$secretData->secretVersion);
|
|||
|
$result[$value] = $this->securityUtil->decrypt($value,$type,$appContext);
|
|||
|
$this->incrCounter(2,$type,$appContext,false);
|
|||
|
$this->flushCounter($appContext);
|
|||
|
} else {
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,$secretData->secretVersion);
|
|||
|
$result[$value] = $this->securityUtil->decrypt($value,$type,$secretContext);
|
|||
|
$this->incrCounter(2,$type,$secretContext,false);
|
|||
|
$this->flushCounter($secretContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return $result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 使用上一版本秘钥解密,app级别公钥
|
|||
|
*/
|
|||
|
function decryptPreviousPublic($data,$type)
|
|||
|
{
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,-1);
|
|||
|
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 使用上一版本秘钥解密,一般只用于更新秘钥
|
|||
|
*/
|
|||
|
function decryptPrevious($data,$type,$session)
|
|||
|
{
|
|||
|
if($this->securityUtil->isPublicData($data,$type)){
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,-1);
|
|||
|
} else {
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,-1);
|
|||
|
}
|
|||
|
return $this->securityUtil->decrypt($data,$type,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 加密单条数据,使用app级别公钥
|
|||
|
*/
|
|||
|
function encryptPublic($data,$type,$version = null)
|
|||
|
{
|
|||
|
return $this->encrypt($data,$type,null,$version);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 加密单条数据
|
|||
|
*/
|
|||
|
function encrypt($data,$type,$session = null,$version = null)
|
|||
|
{
|
|||
|
if(empty($data) || empty($type)){
|
|||
|
return null;
|
|||
|
}
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,null);
|
|||
|
$this->incrCounter(1,$type,$secretContext,true);
|
|||
|
|
|||
|
return $this->securityUtil->encrypt($data,$type,$version,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 加密多条数据,使用app级别公钥
|
|||
|
*/
|
|||
|
function encryptBatchPublic($array,$type,$version = null)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return null;
|
|||
|
}
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,null);
|
|||
|
$result = array();
|
|||
|
foreach ($array as $value) {
|
|||
|
$result[$value] = $this->securityUtil->encrypt($value,$type,$version,$secretContext);
|
|||
|
$this->incrCounter(1,$type,$secretContext,false);
|
|||
|
}
|
|||
|
$this->flushCounter($secretContext);
|
|||
|
|
|||
|
return $result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 加密多条数据,必须是同一个type和用户,返回结果是 KV结果
|
|||
|
*/
|
|||
|
function encryptBatch($array,$type,$session,$version = null)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return null;
|
|||
|
}
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,null);
|
|||
|
$result = array();
|
|||
|
foreach ($array as $value) {
|
|||
|
$result[$value] = $this->securityUtil->encrypt($value,$type,$version,$secretContext);
|
|||
|
$this->incrCounter(1,$type,$secretContext,false);
|
|||
|
}
|
|||
|
$this->flushCounter($secretContext);
|
|||
|
return $result;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 使用上一版本秘钥加密,使用app级别公钥
|
|||
|
*/
|
|||
|
function encryptPreviousPublic($data,$type)
|
|||
|
{
|
|||
|
$secretContext = $this->callSecretApiWithCache(null,-1);
|
|||
|
$this->incrCounter(1,$type,$secretContext,true);
|
|||
|
|
|||
|
return $this->securityUtil->encrypt($data,$type,$secretContext->version,$secretContext);
|
|||
|
}
|
|||
|
/**
|
|||
|
* 使用上一版本秘钥加密,一般只用于更新秘钥
|
|||
|
*/
|
|||
|
function encryptPrevious($data,$type,$session)
|
|||
|
{
|
|||
|
$secretContext = $this->callSecretApiWithCache($session,-1);
|
|||
|
$this->incrCounter(1,$type,$secretContext,true);
|
|||
|
|
|||
|
return $this->securityUtil->encrypt($data,$type,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据session生成秘钥
|
|||
|
*/
|
|||
|
function initSecret($session)
|
|||
|
{
|
|||
|
return $this->callSecretApiWithCache($session,null);
|
|||
|
}
|
|||
|
|
|||
|
function buildCacheKey($session,$secretVersion)
|
|||
|
{
|
|||
|
if(empty($session)){
|
|||
|
return $this->topClient->getAppkey();
|
|||
|
}
|
|||
|
if(empty($secretVersion)){
|
|||
|
return $session ;
|
|||
|
}
|
|||
|
return $session.'_'.$secretVersion ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
function generateCustomerSession($userId)
|
|||
|
{
|
|||
|
return '_'.$userId ;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 判断是否是已加密的数据
|
|||
|
*/
|
|||
|
function isEncryptData($data,$type)
|
|||
|
{
|
|||
|
if(empty($data) || empty($type)){
|
|||
|
return false;
|
|||
|
}
|
|||
|
return $this->securityUtil->isEncryptData($data,$type);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 判断是否是已加密的数据,数据必须是同一个类型
|
|||
|
*/
|
|||
|
function isEncryptDataArray($array,$type)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return false;
|
|||
|
}
|
|||
|
return $this->securityUtil->isEncryptDataArray($array,$type);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 判断数组中的数据是否存在密文,存在任何一个返回true,否则false
|
|||
|
*/
|
|||
|
function isPartEncryptData($array,$type)
|
|||
|
{
|
|||
|
if(empty($array) || empty($type)){
|
|||
|
return false;
|
|||
|
}
|
|||
|
return $this->securityUtil->isPartEncryptData($array,$type);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取秘钥,使用缓存
|
|||
|
*/
|
|||
|
function callSecretApiWithCache($session,$secretVersion)
|
|||
|
{
|
|||
|
if($this->cacheClient)
|
|||
|
{
|
|||
|
$time = time();
|
|||
|
$cacheKey = $this->buildCacheKey($session,$secretVersion);
|
|||
|
$secretContext = $this->cacheClient->getCache($cacheKey);
|
|||
|
|
|||
|
if($secretContext)
|
|||
|
{
|
|||
|
if($this->canUpload($secretContext)){
|
|||
|
if($this->report($secretContext)){
|
|||
|
$this->clearReport($secretContext);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if($secretContext && $secretContext->invalidTime > $time)
|
|||
|
{
|
|||
|
return $secretContext;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$secretContext = $this->callSecretApi($session,$secretVersion);
|
|||
|
|
|||
|
if($this->cacheClient)
|
|||
|
{
|
|||
|
$secretContext->cacheKey = $cacheKey;
|
|||
|
$this->cacheClient->setCache($cacheKey,$secretContext);
|
|||
|
}
|
|||
|
|
|||
|
return $secretContext;
|
|||
|
}
|
|||
|
|
|||
|
function incrCounter($op,$type,$secretContext,$flush)
|
|||
|
{
|
|||
|
if($op == 1){
|
|||
|
switch ($type) {
|
|||
|
case 'nick':
|
|||
|
$secretContext->encryptNickNum ++ ;
|
|||
|
break;
|
|||
|
case 'simple':
|
|||
|
$secretContext->encryptSimpleNum ++ ;
|
|||
|
break;
|
|||
|
case 'receiver_name':
|
|||
|
$secretContext->encryptReceiverNameNum ++ ;
|
|||
|
break;
|
|||
|
case 'phone':
|
|||
|
$secretContext->encryptPhoneNum ++ ;
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}else if($op == 2){
|
|||
|
switch ($type) {
|
|||
|
case 'nick':
|
|||
|
$secretContext->decryptNickNum ++ ;
|
|||
|
break;
|
|||
|
case 'simple':
|
|||
|
$secretContext->decryptSimpleNum ++ ;
|
|||
|
break;
|
|||
|
case 'receiver_name':
|
|||
|
$secretContext->decryptReceiverNameNum ++ ;
|
|||
|
break;
|
|||
|
case 'phone':
|
|||
|
$secretContext->decryptPhoneNum ++ ;
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}else{
|
|||
|
switch ($type) {
|
|||
|
case 'nick':
|
|||
|
$secretContext->searchNickNum ++ ;
|
|||
|
break;
|
|||
|
case 'simple':
|
|||
|
$secretContext->searchSimpleNum ++ ;
|
|||
|
break;
|
|||
|
case 'receiver_name':
|
|||
|
$secretContext->searchReceiverNameNum ++ ;
|
|||
|
break;
|
|||
|
case 'phone':
|
|||
|
$secretContext->searchPhoneNum ++ ;
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if($flush && $this->cacheClient){
|
|||
|
$this->cacheClient->setCache($secretContext->cacheKey,$secretContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function flushCounter($secretContext)
|
|||
|
{
|
|||
|
if($this->cacheClient){
|
|||
|
$this->cacheClient->setCache($secretContext->cacheKey,$secretContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function clearReport($secretContext)
|
|||
|
{
|
|||
|
$secretContext->encryptPhoneNum = 0;
|
|||
|
$secretContext->encryptNickNum = 0;
|
|||
|
$secretContext->encryptReceiverNameNum = 0;
|
|||
|
$secretContext->encryptSimpleNum = 0;
|
|||
|
$secretContext->encryptSearchNum = 0;
|
|||
|
$secretContext->decryptPhoneNum = 0;
|
|||
|
$secretContext->decryptNickNum = 0;
|
|||
|
$secretContext->decryptReceiverNameNum = 0;
|
|||
|
$secretContext->decryptSimpleNum = 0;
|
|||
|
$secretContext->decryptSearchNum = 0;
|
|||
|
$secretContext->searchPhoneNum = 0;
|
|||
|
$secretContext->searchNickNum = 0;
|
|||
|
$secretContext->searchReceiverNameNum = 0;
|
|||
|
$secretContext->searchSimpleNum = 0;
|
|||
|
$secretContext->searchSearchNum = 0;
|
|||
|
$secretContext->lastUploadTime = time();
|
|||
|
}
|
|||
|
|
|||
|
function canUpload($secretContext)
|
|||
|
{
|
|||
|
$current = time();
|
|||
|
if($current - $secretContext->lastUploadTime > 300){
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 上报信息
|
|||
|
*/
|
|||
|
function report($secretContext)
|
|||
|
{
|
|||
|
$request = new TopSdkFeedbackUploadRequest;
|
|||
|
$request->setContent($secretContext->toLogString());
|
|||
|
|
|||
|
if(empty($secretContext->session)){
|
|||
|
$request->setType(APP_SECRET_TYPE);
|
|||
|
}else{
|
|||
|
$request->setType(APP_USER_SECRET_TYPE);
|
|||
|
}
|
|||
|
|
|||
|
$response = $this->topClient->execute($request,$secretContext->session);
|
|||
|
if($response->code == 0){
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取秘钥,不使用缓存
|
|||
|
*/
|
|||
|
function callSecretApi($session,$secretVersion)
|
|||
|
{
|
|||
|
$request = new TopSecretGetRequest;
|
|||
|
$request->setRandomNum($this->randomNum);
|
|||
|
if($secretVersion)
|
|||
|
{
|
|||
|
if(intval($secretVersion) < 0 || $session == null){
|
|||
|
$session = null;
|
|||
|
$secretVersion = -1 * intval($secretVersion < 0);
|
|||
|
}
|
|||
|
$request->setSecretVersion($secretVersion);
|
|||
|
}
|
|||
|
|
|||
|
$topSession = $session;
|
|||
|
if($session != null && $session[0] == '_')
|
|||
|
{
|
|||
|
$request->setCustomerUserId(substr($session,1));
|
|||
|
$topSession = null;
|
|||
|
}
|
|||
|
|
|||
|
$response = $this->topClient->execute($request,$topSession);
|
|||
|
if($response->code != 0){
|
|||
|
throw new Exception($response->msg);
|
|||
|
}
|
|||
|
|
|||
|
$time = time();
|
|||
|
$secretContext = new SecretContext();
|
|||
|
$secretContext->maxInvalidTime = $time + intval($response->max_interval);
|
|||
|
$secretContext->invalidTime = $time + intval($response->interval);
|
|||
|
$secretContext->secret = strval($response->secret);
|
|||
|
$secretContext->session = $session;
|
|||
|
if(!empty($response->app_config)){
|
|||
|
$tmpJson = json_decode($response->app_config);
|
|||
|
$appConfig = array();
|
|||
|
foreach ($tmpJson as $key => $value){
|
|||
|
$appConfig[$key] = $value;
|
|||
|
}
|
|||
|
$secretContext->appConfig = $appConfig;
|
|||
|
}
|
|||
|
|
|||
|
if(empty($session)){
|
|||
|
$secretContext->secretVersion = -1 * intval($response->secret_version);
|
|||
|
}else{
|
|||
|
$secretContext->secretVersion = intval($response->secret_version);
|
|||
|
}
|
|||
|
return $secretContext;
|
|||
|
}
|
|||
|
}
|
|||
|
?>
|