├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── src └── TLSSigAPI.php └── tests └── test.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.phar 2 | /vendor/ 3 | 4 | # Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file 5 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file 6 | # composer.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 腾讯云 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## composer 集成 2 | ```json 3 | { 4 | "require": { 5 | "tencent/tls-sig-api": "1.0" 6 | } 7 | } 8 | ``` 9 | 10 | 11 | ## 调用接口 12 | 13 | ### 默认过期时间 14 | ```php 15 | require 'vendor/autoload.php'; 16 | 17 | $api = new Tencent\TLSSigAPI(); 18 | $api->SetAppid(140000000); 19 | $private = file_get_contents(dirname(__FILE__).DIRECTORY_SEPARATOR.'private_key'); 20 | $api->SetPrivateKey($private); 21 | $sig = $api->genSig('xiaojun'); 22 | var_export($sig); 23 | ``` 24 | 25 | ### 指定过期时间 26 | ```php 27 | require 'vendor/autoload.php'; 28 | 29 | $api = new Tencent\TLSSigAPI(); 30 | $api->SetAppid(140000000); 31 | $private = file_get_contents(dirname(__FILE__).DIRECTORY_SEPARATOR.'private_key'); 32 | $api->SetPrivateKey($private); 33 | $sig = $api->genSig('xiaojun', 24*3600*180); 34 | var_export($sig); 35 | ``` 36 | 37 | ### 源代码集成 38 | ```php 39 | require 'vendor/autoload.php'; 40 | ``` 41 | 直接上面的 ```require``` 语句替换为下面的 42 | ```php 43 | require "TLSSigAPI.php"; 44 | ``` 45 | 注意语句的路径需要随着 api 文件的存放路径而修改。 -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tencent/tls-sig-api", 3 | "type": "library", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "okhowang(王沛文)", 8 | "email": "okhowang@tencent.com" 9 | } 10 | ], 11 | "require": {}, 12 | "require-dev": { 13 | "phpunit/phpunit": "^4" 14 | }, 15 | "autoload": { 16 | "psr-4": {"Tencent\\": "src/"} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/TLSSigAPI.php: -------------------------------------------------------------------------------- 1 | = 0 && !in_array('secp256k1', openssl_get_curve_names(), true)) { 18 | trigger_error('not support secp256k1', E_USER_NOTICE); 19 | } 20 | 21 | class TLSSigAPI { 22 | 23 | private $private_key = false; 24 | private $public_key = false; 25 | private $appid = 0; 26 | 27 | /** 28 | * 设置Appid 29 | * @param type $appid 30 | */ 31 | public function setAppid($appid) { 32 | $this->appid = $appid; 33 | } 34 | 35 | /** 36 | * 设置私钥 如果要生成usersig则需要私钥 37 | * @param string $private_key 私钥文件内容 38 | * @return bool 是否成功 39 | */ 40 | public function setPrivateKey($private_key) { 41 | $this->private_key = openssl_pkey_get_private($private_key); 42 | if ($this->private_key === false) { 43 | throw new \Exception(openssl_error_string()); 44 | } 45 | return true; 46 | } 47 | 48 | /** 49 | * 设置公钥 如果要验证usersig则需要公钥 50 | * @param string $public_key 公钥文件内容 51 | * @return bool 是否成功 52 | */ 53 | public function setPublicKey($public_key) { 54 | $this->public_key = openssl_pkey_get_public($public_key); 55 | if ($this->public_key === false) { 56 | throw new \Exception(openssl_error_string()); 57 | } 58 | return true; 59 | } 60 | 61 | /** 62 | * 用于url的base64encode 63 | * '+' => '*', '/' => '-', '=' => '_' 64 | * @param string $string 需要编码的数据 65 | * @return string 编码后的base64串,失败返回false 66 | */ 67 | private function base64Encode($string) { 68 | static $replace = Array('+' => '*', '/' => '-', '=' => '_'); 69 | $base64 = base64_encode($string); 70 | if ($base64 === false) { 71 | throw new \Exception('base64_encode error'); 72 | } 73 | return str_replace(array_keys($replace), array_values($replace), $base64); 74 | } 75 | 76 | /** 77 | * 用于url的base64decode 78 | * '+' => '*', '/' => '-', '=' => '_' 79 | * @param string $base64 需要解码的base64串 80 | * @return string 解码后的数据,失败返回false 81 | */ 82 | private function base64Decode($base64) { 83 | static $replace = Array('+' => '*', '/' => '-', '=' => '_'); 84 | $string = str_replace(array_values($replace), array_keys($replace), $base64); 85 | $result = base64_decode($string); 86 | if ($result == false) { 87 | throw new \Exception('base64_decode error'); 88 | } 89 | return $result; 90 | } 91 | 92 | /** 93 | * 根据json内容生成需要签名的buf串 94 | * @param array $json 票据json对象 95 | * @return string 按标准格式生成的用于签名的字符串 96 | * 失败时返回false 97 | */ 98 | private function genSignContent(array $json) { 99 | $content = ''; 100 | static $aid3rd = 'TLS.appid_at_3rd'; 101 | if(isset($json[$aid3rd])) { 102 | $content .= "{$aid3rd}:{$json[$aid3rd]}\n"; 103 | } 104 | static $members = Array( 105 | 'TLS.account_type', 106 | 'TLS.identifier', 107 | 'TLS.sdk_appid', 108 | 'TLS.time', 109 | 'TLS.expire_after' 110 | ); 111 | foreach ($members as $member) { 112 | if (!isset($json[$member])) { 113 | throw new \Exception('json need ' . $member); 114 | } 115 | $content .= "{$member}:{$json[$member]}\n"; 116 | } 117 | return $content; 118 | } 119 | 120 | /** 121 | * ECDSA-SHA256签名 122 | * @param string $data 需要签名的数据 123 | * @return string 返回签名 失败时返回false 124 | */ 125 | private function sign($data) { 126 | $signature = ''; 127 | if (!openssl_sign($data, $signature, $this->private_key, 'sha256')) { 128 | throw new \Exception(openssl_error_string()); 129 | } 130 | return $signature; 131 | } 132 | 133 | /** 134 | * 验证ECDSA-SHA256签名 135 | * @param string $data 需要验证的数据原文 136 | * @param string $sig 需要验证的签名 137 | * @return int 1验证成功 0验证失败 138 | */ 139 | private function verify($data, $sig) { 140 | $ret = openssl_verify($data, $sig, $this->public_key, 'sha256'); 141 | if ($ret == -1) { 142 | throw new \Exception(openssl_error_string()); 143 | } 144 | return $ret; 145 | } 146 | 147 | /** 148 | * 生成usersig 149 | * @param string $identifier 用户名 150 | * @param uint $expire usersig有效期 默认为180天 151 | * @return string 生成的UserSig 失败时为false 152 | */ 153 | public function genSig($identifier, $expire = 15552000) { 154 | $json = Array( 155 | 'TLS.account_type' => '0', 156 | 'TLS.identifier' => (string) $identifier, 157 | 'TLS.appid_at_3rd' => '0', 158 | 'TLS.sdk_appid' => (string) $this->appid, 159 | 'TLS.expire_after' => (string) $expire, 160 | 'TLS.version' => '201512300000', 161 | 'TLS.time' => (string) time() 162 | ); 163 | $err = ''; 164 | $content = $this->genSignContent($json, $err); 165 | $signature = $this->sign($content, $err); 166 | $json['TLS.sig'] = base64_encode($signature); 167 | if ($json['TLS.sig'] === false) { 168 | throw new \Exception('base64_encode error'); 169 | } 170 | $json_text = json_encode($json); 171 | if ($json_text === false) { 172 | throw new \Exception('json_encode error'); 173 | } 174 | $compressed = gzcompress($json_text); 175 | if ($compressed === false) { 176 | throw new \Exception('gzcompress error'); 177 | } 178 | return $this->base64Encode($compressed); 179 | } 180 | 181 | /** 182 | * 验证usersig 183 | * @param type $sig usersig 184 | * @param type $identifier 需要验证用户名 185 | * @param type $init_time usersig中的生成时间 186 | * @param type $expire_time usersig中的有效期 如:3600秒 187 | * @param type $error_msg 失败时的错误信息 188 | * @return boolean 验证是否成功 189 | */ 190 | public function verifySig($sig, $identifier, &$init_time, &$expire_time, &$error_msg) { 191 | try { 192 | $error_msg = ''; 193 | $decoded_sig = $this->base64Decode($sig); 194 | $uncompressed_sig = gzuncompress($decoded_sig); 195 | if ($uncompressed_sig === false) { 196 | throw new \Exception('gzuncompress error'); 197 | } 198 | $json = json_decode($uncompressed_sig); 199 | if ($json == false) { 200 | throw new \Exception('json_decode error'); 201 | } 202 | $json = (array) $json; 203 | if ($json['TLS.identifier'] !== $identifier) { 204 | throw new \Exception("identifier error sigid:{$json['TLS.identifier']} id:{$identifier}"); 205 | } 206 | if ($json['TLS.sdk_appid'] != $this->appid) { 207 | throw new \Exception("appid error sigappid:{$json['TLS.appid']} thisappid:{$this->appid}"); 208 | } 209 | $content = $this->genSignContent($json); 210 | $signature = base64_decode($json['TLS.sig']); 211 | if ($signature == false) { 212 | throw new \Exception('sig json_decode error'); 213 | } 214 | $succ = $this->verify($content, $signature); 215 | if (!$succ) { 216 | throw new \Exception('verify failed'); 217 | } 218 | $init_time = $json['TLS.time']; 219 | $expire_time = $json['TLS.expire_after']; 220 | return true; 221 | } catch (\Exception $ex) { 222 | $error_msg = $ex->getMessage(); 223 | return false; 224 | } 225 | } 226 | /** 227 | * 根据json内容生成需要签名的buf串 228 | * @param array $json 票据json对象 229 | * @return string 按标准格式生成的用于签名的字符串 230 | * 失败时返回false 231 | */ 232 | private function genSignContentWithUserbuf(array $json) { 233 | static $members = Array( 234 | 'TLS.appid_at_3rd', 235 | 'TLS.account_type', 236 | 'TLS.identifier', 237 | 'TLS.sdk_appid', 238 | 'TLS.time', 239 | 'TLS.expire_after', 240 | 'TLS.userbuf' 241 | ); 242 | $content = ''; 243 | foreach ($members as $member) { 244 | if (!isset($json[$member])) { 245 | throw new \Exception('json need ' . $member); 246 | } 247 | $content .= "{$member}:{$json[$member]}\n"; 248 | } 249 | return $content; 250 | } 251 | 252 | /** 253 | * 生成usersig 254 | * @param string $identifier 用户名 255 | * @param uint $expire usersig有效期 默认为180天 256 | * @return string 生成的UserSig 失败时为false 257 | */ 258 | public function genSigWithUserbuf($identifier, $userbuf, $expire = 15552000) { 259 | $json = Array( 260 | 'TLS.account_type' => '0', 261 | 'TLS.identifier' => (string) $identifier, 262 | 'TLS.appid_at_3rd' => '0', 263 | 'TLS.sdk_appid' => (string) $this->appid, 264 | 'TLS.expire_after' => (string) $expire, 265 | 'TLS.version' => '201512300000', 266 | 'TLS.time' => (string) time(), 267 | 'TLS.userbuf' => base64_encode($userbuf) 268 | ); 269 | $err = ''; 270 | $content = $this->genSignContentWithUserbuf($json, $err); 271 | $signature = $this->sign($content, $err); 272 | $json['TLS.sig'] = base64_encode($signature); 273 | if ($json['TLS.sig'] === false) { 274 | throw new \Exception('base64_encode error'); 275 | } 276 | $json_text = json_encode($json); 277 | if ($json_text === false) { 278 | throw new \Exception('json_encode error'); 279 | } 280 | $compressed = gzcompress($json_text); 281 | if ($compressed === false) { 282 | throw new \Exception('gzcompress error'); 283 | } 284 | return $this->base64Encode($compressed); 285 | } 286 | 287 | /** 288 | * 验证usersig 289 | * @param type $sig usersig 290 | * @param type $identifier 需要验证用户名 291 | * @param type $init_time usersig中的生成时间 292 | * @param type $expire_time usersig中的有效期 如:3600秒 293 | * @param type $error_msg 失败时的错误信息 294 | * @return boolean 验证是否成功 295 | */ 296 | public function verifySigWithUserbuf($sig, $identifier, &$init_time, &$expire_time, &$userbuf, &$error_msg) { 297 | try { 298 | $error_msg = ''; 299 | $decoded_sig = $this->base64Decode($sig); 300 | $uncompressed_sig = gzuncompress($decoded_sig); 301 | if ($uncompressed_sig === false) { 302 | throw new \Exception('gzuncompress error'); 303 | } 304 | $json = json_decode($uncompressed_sig); 305 | if ($json == false) { 306 | throw new \Exception('json_decode error'); 307 | } 308 | $json = (array) $json; 309 | if ($json['TLS.identifier'] !== $identifier) { 310 | throw new \Exception("identifier error sigid:{$json['TLS.identifier']} id:{$identifier}"); 311 | } 312 | if ($json['TLS.sdk_appid'] != $this->appid) { 313 | throw new \Exception("appid error sigappid:{$json['TLS.appid']} thisappid:{$this->appid}"); 314 | } 315 | $content = $this->genSignContentWithUserbuf($json); 316 | $signature = base64_decode($json['TLS.sig']); 317 | if ($signature == false) { 318 | throw new \Exception('sig json_decode error'); 319 | } 320 | $succ = $this->verify($content, $signature); 321 | if (!$succ) { 322 | throw new \Exception('verify failed'); 323 | } 324 | $init_time = $json['TLS.time']; 325 | $expire_time = $json['TLS.expire_after']; 326 | $userbuf = base64_decode($json['TLS.userbuf']); 327 | return true; 328 | } catch (\Exception $ex) { 329 | $error_msg = $ex->getMessage(); 330 | return false; 331 | } 332 | } 333 | 334 | } 335 | -------------------------------------------------------------------------------- /tests/test.php: -------------------------------------------------------------------------------- 1 | SetAppid(1400001052);//设置在腾讯云申请的appid 29 | $api->SetPrivateKey(self::$private_key_string);//生成usersig需要先设置私钥 30 | $api->SetPublicKey(self::$public_key_string);//校验usersig需要先设置公钥 31 | $sig = $api->genSig('user1');//生成usersig 32 | $result = $api->verifySig($sig, 'user1', $init_time, $expire_time, $error_msg);//校验usersig 33 | $this->assertEquals(true, $result); 34 | 35 | $result = $api->verifySig($sig, 'user2', $init_time, $expire_time, $error_msg); 36 | $this->assertEquals(false, $result); 37 | } 38 | 39 | public function testGenAndVerifyUserbuf() 40 | { 41 | $api = new TLSSigAPI(); 42 | $api->SetAppid(1400001052);//设置在腾讯云申请的appid 43 | $api->SetPrivateKey(self::$private_key_string);//生成usersig需要先设置私钥 44 | $api->SetPublicKey(self::$public_key_string);//校验usersig需要先设置公钥 45 | $sig = $api->genSigWithUserbuf('user1', 'buf');//生成usersig 46 | $result = $api->verifySigWithUserbuf($sig, 'user1', $init_time, $expire_time, $userbuf, $error_msg);//校验usersig 47 | $this->assertEquals(true, $result); 48 | $this->assertEquals('buf', $userbuf); 49 | 50 | $result = $api->verifySigWithUserbuf($sig, 'user2', $init_time, $expire_time, $userbuf, $error_msg); 51 | $this->assertEquals(false, $result); 52 | } 53 | 54 | public function testVerify() 55 | { 56 | $api = new TLSSigAPI(); 57 | $api->SetAppid(1400001052);//设置在腾讯云申请的appid 58 | $api->SetPublicKey(self::$public_key_string);//校验usersig需要先设置公钥 59 | $result = $api->verifySig('eAFNjlFvgjAUhf9LX1lGbxFZl-iwWGcUt8jQsflCOlpdJwKBguiy-24lmOw8ft89J-cXrRbhPU*SvM50rE*FRI8Io7sOKyEzrbZKlgbyr6THvCiUiLmOnVL8u67EPu6UYTDAJoBd0ndkW6hSxnyruzF6S68bWVYqz0yTYHjADnGu-V5qdbh*BS4BSgHobbNSO4NfJuvxLGCrd8Ws*ZPX*lEz9vYhnZwHDXx6wfLNXnrpccqzj5bZ6Tk8znbR9w-Poa3hdQgsPwQMnjV2N-bGF9O5X5M0stbEWqhhHYzQ3wVIlVX1', 'abc', $init_time, $expire_time, $error_msg);//校验usersig 60 | echo $error_msg; 61 | $this->assertEquals(true, $result); 62 | 63 | $result = $api->verifySigWithUserbuf('eAE1jl1vgjAYhf9Lb1lcKxDHEi*YNh2ESZiK86ppS9GKH3y028zifx*Yei6f55y87x9YJctRV1SU1bUqwCtAHuyDoD8GT3epCnnWqlSy7S3jwmKtTnKo*2MUBGjieZbL31q1krJS3weBjbVMiIs5a6qv9bCGDzwcp0xTtx1*eGDTyZabsifbTXyw3U7tevCB17OIVE0a7k38HhHnJU4LPk*Mm2FhMp6S*dfMeb5KlnkIbT9DhUNN4twngawgdJsjz8X3m1iECV7nBwMvG3yKFs6**dktiZiC2z-8z1UA', 'abc', $init_time, $expire_time, $userbuf, $error_msg); 64 | $this->assertEquals(true, $result); 65 | $this->assertEquals('abc', $userbuf); 66 | } 67 | 68 | public function testWithout3rd() 69 | { 70 | $api = new TLSSigAPI(); 71 | $api->SetAppid(1400000226);//设置在腾讯云申请的appid 72 | $api->SetPublicKey(<<<'EOT' 73 | -----BEGIN PUBLIC KEY----- 74 | MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAED5Ffi4qIe4XUZ5zDGR9pC0Z6UL/gCHf0 75 | vgoLVestQxqOGJB5mcbaKULeriaevZoq0Sx8gGtfDlSf4fXwzPtGvg== 76 | -----END PUBLIC KEY----- 77 | EOT 78 | );//校验usersig需要先设置公钥 79 | $result = $api->verifySig('eJxNz11vgjAYBeB7fkXTW41rKwL1bkMMGnGyidu8IaxUfdehFWpkMfvv8ysL5-Y5yck5WQghPJ*8djIhdoetSc2Plhj1EabUwe1-lrWGUqbZysjyyqzHGSGkUYFcbg2s4F6gZ6UNrnKVZlpDflObXMJYc6SC9RWj4MMfxf57lz0xb8ztcd2aKgrTJYdSh8lsU8dmcnQTFaqdGC48dRxtHp8DL5qLw-fnW*Q8BCJosXj-FfpLx7eLRf2iOUnWVTEgXuI2Jg0U97s9do7LXY6tX*sPGj5LXg__', '10001', $init_time, $expire_time, $error_msg);//校验usersig 80 | $this->assertEquals(true, $result); 81 | } 82 | } 83 | --------------------------------------------------------------------------------