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; } } ?>