TLSSigAPI.class.php
7.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
<?php
/**
* Created by PhpStorm.
* User: zhoutao
* Date: 2018/1/15
* Time: 下午3:05
*/
namespace Com\IM;
class TLSSigAPI
{
private $private_key = false;
private $public_key = false;
private $appid = 0;
/**
* 实例化
*
* @return TLSSigAPI
*/
public static function &instance()
{
static $instance;
if (empty($instance)) {
$instance = new self();
}
return $instance;
}
public function __construct()
{
$this->appid = cfg('TENCENT_IM_SDK_APPID');
$this->private_key = file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Keys/Private_key');
$this->public_key = file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Keys/Public_key');
}
/**
* 设置私钥 如果要生成usersig则需要私钥
* @param string $private_key 私钥文件内容
* @return bool 是否成功
* @throws \Exception
*/
public function setPrivateKey($private_key)
{
$this->private_key = openssl_pkey_get_private($private_key);
if ($this->private_key === false) {
throw new \Exception(openssl_error_string());
}
return true;
}
/**
* 设置公钥 如果要验证usersig则需要公钥
* @param string $public_key 公钥文件内容
* @return bool 是否成功
* @throws \Exception
*/
public function setPublicKey($public_key)
{
$this->public_key = openssl_pkey_get_public($public_key);
if ($this->public_key === false) {
throw new \Exception(openssl_error_string());
}
return true;
}
/**
* 用于url的base64encode
* '+' => '*', '/' => '-', '=' => '_'
* @param string $string 需要编码的数据
* @return string 编码后的base64串,失败返回false
* @throws \Exception
*/
private function base64Encode($string)
{
static $replace = Array('+' => '*', '/' => '-', '=' => '_');
$base64 = base64_encode($string);
if ($base64 === false) {
throw new \Exception('base64_encode error');
}
return str_replace(array_keys($replace), array_values($replace), $base64);
}
/**
* 用于url的base64decode
* '+' => '*', '/' => '-', '=' => '_'
* @param string $base64 需要解码的base64串
* @return string 解码后的数据,失败返回false
* @throws \Exception
*/
private function base64Decode($base64)
{
static $replace = Array('+' => '*', '/' => '-', '=' => '_');
$string = str_replace(array_values($replace), array_keys($replace), $base64);
$result = base64_decode($string);
if ($result == false) {
throw new \Exception('base64_decode error');
}
return $result;
}
/**
* 根据json内容生成需要签名的buf串
* @param array $json 票据json对象
* @return string 按标准格式生成的用于签名的字符串
* 失败时返回false
* @throws \Exception
*/
private function genSignContent(array $json)
{
static $members = Array(
'TLS.appid_at_3rd',
'TLS.account_type',
'TLS.identifier',
'TLS.sdk_appid',
'TLS.time',
'TLS.expire_after',
);
$content = '';
foreach ($members as $member) {
if (!isset($json[$member])) {
throw new \Exception('json need ' . $member);
}
$content .= "{$member}:{$json[$member]}\n";
}
return $content;
}
/**
* ECDSA-SHA256签名
* @param string $data 需要签名的数据
* @return string 返回签名 失败时返回false
* @throws \Exception
*/
private function sign($data)
{
$signature = '';
if (!openssl_sign($data, $signature, $this->private_key, 'sha256')) {
throw new \Exception(openssl_error_string());
}
return $signature;
}
/**
* 验证ECDSA-SHA256签名
* @param string $data 需要验证的数据原文
* @param string $sig 需要验证的签名
* @return int 1验证成功 0验证失败
* @throws \Exception
*/
private function verify($data, $sig)
{
$ret = openssl_verify($data, $sig, $this->public_key, 'sha256');
if ($ret == - 1) {
throw new \Exception(openssl_error_string());
}
return $ret;
}
/**
* 生成usersig
* @param string $identifier 用户名
* @param uint $expire usersig有效期 默认为180天
* @return string 生成的UserSig 失败时为false
* @throws \Exception
*/
public function genSig($identifier, $expire = 180 * 24 * 3600)
{
$json = Array(
'TLS.account_type' => '0',
'TLS.identifier' => (string)$identifier,
'TLS.appid_at_3rd' => '0',
'TLS.sdk_appid' => (string)$this->appid,
'TLS.expire_after' => (string)$expire,
'TLS.version' => '201512300000',
'TLS.time' => (string)time(),
);
$err = '';
$content = $this->genSignContent($json, $err);
$signature = $this->sign($content, $err);
$json['TLS.sig'] = base64_encode($signature);
if ($json['TLS.sig'] === false) {
throw new \Exception('base64_encode error');
}
$json_text = json_encode($json);
if ($json_text === false) {
throw new \Exception('json_encode error');
}
$compressed = gzcompress($json_text);
if ($compressed === false) {
throw new \Exception('gzcompress error');
}
return $this->base64Encode($compressed);
}
/**
* 验证usersig
* @param type $sig usersig
* @param type $identifier 需要验证用户名
* @param type $init_time usersig中的生成时间
* @param type $expire_time usersig中的有效期 如:3600秒
* @param type $error_msg 失败时的错误信息
* @return boolean 验证是否成功
*/
public function verifySig($sig, $identifier, &$init_time, &$expire_time, &$error_msg)
{
try {
$error_msg = '';
$decoded_sig = $this->base64Decode($sig);
$uncompressed_sig = gzuncompress($decoded_sig);
if ($uncompressed_sig === false) {
throw new \Exception('gzuncompress error');
}
$json = json_decode($uncompressed_sig);
if ($json == false) {
throw new \Exception('json_decode error');
}
$json = (array)$json;
if ($json['TLS.identifier'] !== $identifier) {
throw new \Exception("identifier error sigid:{$json['TLS.identifier']} id:{$identifier}");
}
if ($json['TLS.sdk_appid'] != $this->appid) {
throw new \Exception("appid error sigappid:{$json['TLS.appid']} thisappid:{$this->appid}");
}
$content = $this->genSignContent($json);
$signature = base64_decode($json['TLS.sig']);
if ($signature == false) {
throw new \Exception('sig json_decode error');
}
$succ = $this->verify($content, $signature);
if (!$succ) {
throw new \Exception('verify failed');
}
$init_time = $json['TLS.time'];
$expire_time = $json['TLS.expire_after'];
return true;
} catch (\Exception $ex) {
$error_msg = $ex->getMessage();
return false;
}
}
}