123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- <?php
- include './SecretContext.php';
- include './MagicCrypt.php';
- class SecurityUtil
- {
- private $BASE64_ARRAY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
- private $SEPARATOR_CHAR_MAP;
- function __construct()
- {
- if(!defined("PHONE_SEPARATOR_CHAR"))
- {
- define('PHONE_SEPARATOR_CHAR','$');
- }
- if(!defined("NICK_SEPARATOR_CHAR"))
- {
- define('NICK_SEPARATOR_CHAR','~');
- }
- if(!defined("NORMAL_SEPARATOR_CHAR"))
- {
- define('NORMAL_SEPARATOR_CHAR',chr(1));
- }
- $this->SEPARATOR_CHAR_MAP['nick'] = NICK_SEPARATOR_CHAR;
- $this->SEPARATOR_CHAR_MAP['simple'] = NICK_SEPARATOR_CHAR;
- $this->SEPARATOR_CHAR_MAP['receiver_name'] = NICK_SEPARATOR_CHAR;
- $this->SEPARATOR_CHAR_MAP['search'] = NICK_SEPARATOR_CHAR;
- $this->SEPARATOR_CHAR_MAP['normal'] = NORMAL_SEPARATOR_CHAR;
- $this->SEPARATOR_CHAR_MAP['phone'] = PHONE_SEPARATOR_CHAR;
- }
- /*
- * 判断是否是base64格式的数据
- */
- function isBase64Str($str)
- {
- $strLen = strlen($str);
- for($i = 0; $i < $strLen ; $i++)
- {
- if(!$this->isBase64Char($str[$i]))
- {
- return false;
- }
- }
- return true;
- }
- /*
- * 判断是否是base64格式的字符
- */
- function isBase64Char($char)
- {
- return strpos($this->BASE64_ARRAY,$char) !== false;
- }
- /*
- * 使用sep字符进行trim
- */
- function trimBySep($str,$sep)
- {
- $start = 0;
- $end = strlen($str);
- for($i = 0; $i < $end; $i++)
- {
- if($str[$i] == $sep)
- {
- $start = $i + 1;
- }
- else
- {
- break;
- }
- }
- for($i = $end -1 ; $i >= 0; $i--)
- {
- if($str[$i] == $sep)
- {
- $end = $i - 1;
- }
- else
- {
- break;
- }
- }
- return substr($str,$start,$end);
- }
- function checkEncryptData($dataArray)
- {
- if(count($dataArray) == 2){
- return $this->isBase64Str($dataArray[0]);
- }else{
- return $this->isBase64Str($dataArray[0]) && $this->isBase64Str($dataArray[1]);
- }
- }
- /*
- * 判断是否是加密数据
- */
- function isEncryptDataArray($array,$type)
- {
- foreach ($array as $value) {
- if(!$this->isEncryptData($value,$type)){
- return false;
- }
- }
- return true;
- }
- /**
- * 判断是否是已加密的数据,数据必须是同一个类型
- */
- function isPartEncryptData($array,$type)
- {
- $result = false;
- foreach ($array as $value) {
- if($this->isEncryptData($value,$type)){
- $result = true;
- break;
- }
- }
- return $result;
- }
- /*
- * 判断是否是加密数据
- */
- function isEncryptData($data,$type)
- {
- if(!is_string($data) || strlen($data) < 4)
- {
- return false;
- }
- $separator = $this->SEPARATOR_CHAR_MAP[$type];
- $strlen = strlen($data);
- if($data[0] != $separator || $data[$strlen -1] != $separator)
- {
- return false;
- }
- $dataArray = explode($separator,$this->trimBySep($data,$separator));
- $arrayLength = count($dataArray);
- if($separator == PHONE_SEPARATOR_CHAR)
- {
- if($arrayLength != 3)
- {
- return false;
- }
- if($data[$strlen - 2] == $separator)
- {
- return $this->checkEncryptData($dataArray);
- }
- else
- {
- $version = $dataArray[$arrayLength -1];
- if(is_numeric($version))
- {
- $base64Val = $dataArray[$arrayLength -2];
- return $this->isBase64Str($base64Val);
- }
- }
- }else{
- if($data[strlen($data) - 2] == $separator && $arrayLength == 3)
- {
- return $this->checkEncryptData($dataArray);
- }
- else if($arrayLength == 2)
- {
- return $this->checkEncryptData($dataArray);
- }
- else
- {
- return false;
- }
- }
- }
- function search($data, $type,$secretContext)
- {
- $separator = $this->SEPARATOR_CHAR_MAP[$type];
- if('phone' == $type) {
- if (strlen($data) != 4 ) {
- throw new Exception("phoneNumber error");
- }
- return $separator.$this->hmacMD5EncryptToBase64($data, $secretContext->secret).$separator;
- } else {
- $compressLen = $this->getArrayValue($secretContext->appConfig,'encrypt_index_compress_len',3);
- $slideSize = $this->getArrayValue($secretContext->appConfig,'encrypt_slide_size',4);
- $slideList = $this->getSlideWindows($data, $slideSize);
- $builder = '';
- foreach ($slideList as $slide) {
- $builder .= $this->hmacMD5EncryptToBase64($slide,$secretContext->secret,$compressLen);
- }
- return $builder;
- }
- }
- /*
- * 加密逻辑
- */
- function encrypt($data,$type,$version,$secretContext)
- {
- if(!is_string($data))
- {
- return false;
- }
- $separator = $this->SEPARATOR_CHAR_MAP[$type];
- $isIndexEncrypt = $this->isIndexEncrypt($type,$version,$secretContext);
- if($isIndexEncrypt || $type == "search"){
- if('phone' == $type) {
- return $this->encryptPhoneIndex($data,$separator,$secretContext);
- } else {
- $compressLen = $this->getArrayValue($secretContext->appConfig,'encrypt_index_compress_len',3);
- $slideSize = $this->getArrayValue($secretContext->appConfig,'encrypt_slide_size',4);
- return $this->encryptNormalIndex($data,$compressLen,$slideSize,$separator,$secretContext);
- }
- }else{
- if('phone' == $type) {
- return $this->encryptPhone($data,$separator,$secretContext);
- } else {
- return $this->encryptNormal($data,$separator,$secretContext);
- }
- }
- }
- /*
- * 加密逻辑,手机号码格式
- */
- function encryptPhone($data,$separator,$secretContext)
- {
- $len = strlen($data);
- if($len < 11)
- {
- return $data;
- }
- $prefixNumber = substr($data,0,$len -8);
- $last8Number = substr($data,$len -8,$len);
- return $separator.$prefixNumber.$separator.Security::encrypt($last8Number,$secretContext->secret)
- .$separator.$secretContext->secretVersion.$separator ;
- }
- /*
- * 加密逻辑,非手机号码格式
- */
- function encryptNormal($data,$separator,$secretContext)
- {
- return $separator.Security::encrypt($data,$secretContext->secret)
- .$separator.$secretContext->secretVersion.$separator;
- }
- /*
- * 解密逻辑
- */
- function decrypt($data,$type,$secretContext)
- {
- if(!$this->isEncryptData($data,$type))
- {
- throw new Exception("数据[".$data."]不是类型为[".$type."]的加密数据");
- }
- $dataLen = strlen($data);
- $separator = $this->SEPARATOR_CHAR_MAP[$type];
- $secretData = null;
- if($data[$dataLen - 2] == $separator){
- $secretData = $this->getIndexSecretData($data,$separator);
- }else{
- $secretData = $this->getSecretData($data,$separator);
- }
-
- if($secretData == null){
- return $data;
- }
- $result = Security::decrypt($secretData->originalBase64Value,$secretContext->secret);
- if($separator == PHONE_SEPARATOR_CHAR && !$secretData->search)
- {
- return $secretData->originalValue.$result;
- }
- return $result;
- }
- /*
- * 判断是否是公钥数据
- */
- function isPublicData($data,$type)
- {
- $secretData = $this->getSecretDataByType($data,$type);
- if(empty($secretData)){
- return false;
- }
- if(intval($secretData->secretVersion) < 0){
- return true;
- }
- return false;
- }
- function getSecretDataByType($data,$type)
- {
- $separator = $this->SEPARATOR_CHAR_MAP[$type];
- $dataLen = strlen($data);
- if($data[$dataLen - 2] == $separator){
- return $secretData = $this->getIndexSecretData($data,$separator);
- }else{
- return $secretData = $this->getSecretData($data,$separator);
- }
- }
- /*
- * 分解密文
- */
- function getSecretData($data,$separator)
- {
- $secretData = new SecretData;
- $dataArray = explode($separator,$this->trimBySep($data,$separator));
- $arrayLength = count($dataArray);
- if($separator == PHONE_SEPARATOR_CHAR)
- {
- if($arrayLength != 3){
- return null;
- }else{
- $version = $dataArray[2];
- if(is_numeric($version))
- {
- $secretData->originalValue = $dataArray[0];
- $secretData->originalBase64Value = $dataArray[1];
- $secretData->secretVersion = $version;
- }
- }
- }
- else
- {
- if($arrayLength != 2){
- return null;
- }else{
- $version = $dataArray[1];
- if(is_numeric($version))
- {
- $secretData->originalBase64Value = $dataArray[0];
- $secretData->secretVersion = $version;
- }
- }
- }
- return $secretData;
- }
- function getIndexSecretData($data,$separator) {
- $secretData = new SecretData;
- $dataArray = explode($separator,$this->trimBySep($data,$separator));
- $arrayLength = count($dataArray);
- if($separator == PHONE_SEPARATOR_CHAR) {
- if ($arrayLength != 3) {
- return null;
- }else{
- $version = $dataArray[2];
- if(is_numeric($version))
- {
- $secretData->originalValue = $dataArray[0];
- $secretData->originalBase64Value = $dataArray[1];
- $secretData->secretVersion = $version;
- }
- }
-
- } else {
- if($arrayLength != 3){
- return null;
- } else {
- $version = $dataArray[2];
- if(is_numeric($version))
- {
- $secretData->originalBase64Value = $dataArray[0];
- $secretData->originalValue = $dataArray[1];
- $secretData->secretVersion = $version;
- }
- }
- }
- $secretData->search = true;
- return $secretData;
- }
- /**
- * 判断密文是否支持检索
- *
- * @param key
- * @param version
- * @return
- */
- function isIndexEncrypt($key,$version,$secretContext)
- {
- if ($version != null && $version < 0) {
- $key = "previous_".$key;
- } else {
- $key = "current_".$key;
- }
- return $secretContext->appConfig != null &&
- array_key_exists($key,$secretContext->appConfig) &&
- $secretContext->appConfig[$key] == "2";
- }
- function isLetterOrDigit($ch)
- {
- $code = ord($ch);
- if (0 <= $code && $code <= 127) {
- return true;
- }
- return false;
- }
- function utf8_strlen($string = null) {
- // 将字符串分解为单元
- preg_match_all("/./us", $string, $match);
- // 返回单元个数
- return count($match[0]);
- }
- function utf8_substr($string,$start,$end) {
- // 将字符串分解为单元
- preg_match_all("/./us", $string, $match);
- // 返回单元个数
- $result = "";
- for($i = $start; $i < $end; $i++){
- $result .= $match[0][$i];
- }
- return $result;
- }
- function utf8_str_at($string,$index) {
- // 将字符串分解为单元
- preg_match_all("/./us", $string, $match);
- // 返回单元个数
- return $match[0][$index];
- }
- function compress($input,$toLength) {
- if($toLength < 0) {
- return null;
- }
- $output = array();
- for($i = 0; $i < $toLength; $i++) {
- $output[$i] = chr(0);
- }
- $input = $this->getBytes($input);
- $inputLength = count($input);
- for ($i = 0; $i < $inputLength; $i++) {
- $index_output = $i % $toLength;
- $output[$index_output] = $output[$index_output] ^ $input[$i];
- }
- return $output;
- }
- /**
- * @see #hmacMD5Encrypt
- *
- * @param encryptText
- * 被签名的字符串
- * @param encryptKey
- * 密钥
- * @param compressLen压缩长度
- * @return
- * @throws Exception
- */
- function hmacMD5EncryptToBase64($encryptText,$encryptKey,$compressLen = 0) {
- $encryptResult = Security::hmac_md5($encryptText,$encryptKey);
- if($compressLen != 0){
- $encryptResult = $this->compress($encryptResult,$compressLen);
- }
- return base64_encode($this->toStr($encryptResult));
- }
- /**
- * 生成滑动窗口
- *
- * @param input
- * @param slideSize
- * @return
- */
- function getSlideWindows($input,$slideSize = 4)
- {
- $endIndex = 0;
- $startIndex = 0;
- $currentWindowSize = 0;
- $currentWindow = null;
- $dataLength = $this->utf8_strlen($input);
- $windows = array();
- while($endIndex < $dataLength || $currentWindowSize > $slideSize)
- {
- $startsWithLetterOrDigit = false;
- if(!empty($currentWindow)){
- $startsWithLetterOrDigit = $this->isLetterOrDigit($this->utf8_str_at($currentWindow,0));
- }
- if($endIndex == $dataLength && $startsWithLetterOrDigit == false){
- break;
- }
- if($currentWindowSize == $slideSize &&
- $startsWithLetterOrDigit == false &&
- $this->isLetterOrDigit($this->utf8_str_at($input,$endIndex))) {
- $endIndex ++;
- $currentWindow = $this->utf8_substr($input,$startIndex,$endIndex);
- $currentWindowSize = 5;
- } else {
- if($endIndex != 0){
- if($startsWithLetterOrDigit){
- $currentWindowSize -= 1;
- }else{
- $currentWindowSize -= 2;
- }
- $startIndex ++;
- }
- while ($currentWindowSize < $slideSize && $endIndex < $dataLength) {
- $currentChar = $this->utf8_str_at($input,$endIndex);
- if ($this->isLetterOrDigit($currentChar)) {
- $currentWindowSize += 1;
- } else {
- $currentWindowSize += 2;
- }
- $endIndex++;
- }
- $currentWindow = $this->utf8_substr($input,$startIndex,$endIndex);
- }
- array_push($windows,$currentWindow);
- }
- return $windows;
- }
- function encryptPhoneIndex($data,$separator,$secretContext) {
- $dataLength = strlen($data);
- if($dataLength < 11) {
- return $data;
- }
- $last4Number = substr($data,$dataLength -4 ,$dataLength);
- return $separator.$this->hmacMD5EncryptToBase64($last4Number,$secretContext->secret).$separator
- .Security::encrypt($data,$secretContext->secret).$separator.$secretContext->secretVersion
- .$separator.$separator;
- }
- function encryptNormalIndex($data,$compressLen,$slideSize,$separator,$secretContext) {
- $slideList = $this->getSlideWindows($data, $slideSize);
- $builder = "";
- foreach ($slideList as $slide) {
- $builder .= $this->hmacMD5EncryptToBase64($slide,$secretContext->secret,$compressLen);
- }
- return $separator.Security::encrypt($data,$secretContext->secret).$separator.$builder.$separator
- .$secretContext->secretVersion.$separator.$separator;
- }
- function getArrayValue($array,$key,$default) {
- if(array_key_exists($key, $array)){
- return $array[$key];
- }
- return $default;
- }
- function getBytes($string) {
- $bytes = array();
- for($i = 0; $i < strlen($string); $i++){
- $bytes[] = ord($string[$i]);
- }
- return $bytes;
- }
- function toStr($bytes) {
- if(!is_array($bytes)){
- return $bytes;
- }
- $str = '';
- foreach($bytes as $ch) {
- $str .= chr($ch);
- }
- return $str;
- }
- }
- ?>
|