GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 486f11...b55f68 )
by t
07:38 queued 01:58
created

Crypto   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 477
Duplicated Lines 0 %

Test Coverage

Coverage 25.77%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 114
dl 0
loc 477
ccs 33
cts 128
cp 0.2577
rs 8.64
c 3
b 1
f 0
wmc 47

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setDn() 0 4 1
A getPrivateDecrypt() 0 11 4
A __construct() 0 10 2
A getPublicDecrypt() 0 11 4
A getPrivateEncrypt() 0 7 2
A generatePair() 0 7 1
A getSignature() 0 7 2
A setDnOrganizationalUnitName() 0 3 1
A setDnOrganizationName() 0 3 1
A getCert() 0 8 1
A setDnLocalityName() 0 3 1
A setDigestAlg() 0 8 2
A getPair() 0 8 2
A getPublicEncrypt() 0 7 2
A setConfig() 0 8 3
A isVerify() 0 6 2
A setPassword() 0 4 1
A setPair() 0 23 6
A setDnCommonName() 0 3 1
A setDnCountryName() 0 3 1
A setDnStateOrProvinceName() 0 3 1
A setDnEmailAddress() 0 3 1
A generateCert() 0 15 2
A setPrivateKeyBits() 0 4 1
A setPrivateKeyType() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like Crypto often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Crypto, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Class Crypto
4
 *
5
 * @link https://www.icy2003.com/
6
 * @author icy2003 <[email protected]>
7
 * @copyright Copyright (c) 2017, icy2003
8
 */
9
namespace icy2003\php\ihelpers;
10
11
use Exception;
12
use icy2003\php\I;
13
use icy2003\php\icomponents\file\LocalFile;
14
15
/**
16
 * 加解密
17
 *
18
 * 对 openssl 扩展的封装
19
 */
20
class Crypto
21
{
22
    /**
23
     * 生成新密钥的配置
24
     *
25
     * @var array
26
     */
27
    protected $_config;
28
29
    /**
30
     * 初始化
31
     *
32
     * - 检测扩展
33
     * - 给密钥配置以默认值,默认为 RSA-SHA256
34
     */
35 5
    public function __construct()
36
    {
37 5
        if (false === I::isExt('openssl')) {
38 5
            throw new Exception("请安装 php_openssl 扩展");
39
        }
40 5
        $this->_config = [
41 5
            'digest_alg' => 'SHA256',
42 5
            'private_key_bits' => 2048,
43 5
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
44 5
            'config' => I::getAlias('@icy2003/php/openssl.cnf'),
45
        ];
46 5
    }
47
48
    /**
49
     * 设置摘要算法或签名哈希算法
50
     *
51
     * @param string $method
52
     *
53
     * @return static
54
     */
55 1
    public function setDigestAlg($method)
56
    {
57 1
        $methods = openssl_get_md_methods();
58 1
        if (!Arrays::in($method, $methods, false, true)) {
59 1
            throw new Exception("不合法的摘要算法");
60
        }
61 1
        $this->_config['digest_alg'] = $method;
62 1
        return $this;
63
    }
64
65
    /**
66
     * 指定应该使用多少位来生成私钥
67
     *
68
     * @param integer $bit
69
     *
70
     * @return static
71
     */
72 1
    public function setPrivateKeyBits($bit)
73
    {
74 1
        $this->_config['private_key_bits'] = $bit;
75 1
        return $this;
76
    }
77
78
    /**
79
     * 选择在创建CSR时应该使用哪些扩展
80
     *
81
     * - 可选值:
82
     *      - OPENSSL_KEYTYPE_DSA
83
     *      - OPENSSL_KEYTYPE_DH
84
     *      - OPENSSL_KEYTYPE_RSA
85
     *      - OPENSSL_KEYTYPE_EC
86
     *
87
     * @param integer $privateKeyType
88
     *
89
     * @return static
90
     */
91 1
    public function setPrivateKeyType($privateKeyType)
92
    {
93
        $types = [
94 1
            OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC,
95
        ];
96 1
        if (!Arrays::in($privateKeyType, $types, false, true)) {
97 1
            throw new Exception("不合法的密钥扩展名");
98
        }
99 1
        $this->_config['private_key_type'] = $privateKeyType;
100 1
        return $this;
101
    }
102
103
    /**
104
     * 自定义 openssl.conf 文件的路径
105
     *
106
     * @param string $config 支持别名
107
     *
108
     * @return static
109
     */
110 1
    public function setConfig($config = null)
111
    {
112 1
        null === $config && $config = I::get($this->_config, 'config');
113 1
        if (false === (new LocalFile())->isFile($configPath = $config)) {
114 1
            throw new Exception("openssl.cnf 文件不存在");
115
        }
116 1
        $this->_config['config'] = I::getAlias($configPath);
117 1
        return $this;
118
    }
119
120
    /**
121
     * pem 格式的私钥
122
     *
123
     * @var string
124
     */
125
    protected $_pemPrivate;
126
    /**
127
     * pem 格式的公钥
128
     *
129
     * @var string
130
     */
131
    protected $_pemPublic;
132
    /**
133
     * 证书密码
134
     *
135
     * @var string
136
     */
137
    protected $_password;
138
139
    /**
140
     * 设置证书密码
141
     *
142
     * @param string $password
143
     *
144
     * @return static
145
     */
146 1
    public function setPassword($password)
147
    {
148 1
        $this->_password = $password;
149 1
        return $this;
150
    }
151
152
    /**
153
     * 生成密钥对
154
     *
155
     * @return static
156
     */
157
    public function generatePair()
158
    {
159
        $privateKeyRes = openssl_pkey_new($this->_config);
160
        openssl_pkey_export($privateKeyRes, $this->_pemPrivate, $this->_password, $this->_config);
161
        $detail = openssl_pkey_get_details($privateKeyRes);
162
        $this->_pemPublic = $detail['key'];
163
        return $this;
164
    }
165
166
    /**
167
     * 获取密钥对
168
     *
169
     * @param boolean $toCompact 是否压缩在一起(去头尾,删除换行),默认 false,即不压缩
170
     *
171
     * @return array
172
     */
173
    public function getPair($toCompact = false)
174
    {
175
        if (false === $toCompact) {
176
            return [$this->_pemPublic, $this->_pemPrivate];
177
        } else {
178
            $publicKeyArr = array_filter(explode("\n", $this->_pemPublic));
179
            $privateKeyArr = array_filter(explode("\n", $this->_pemPrivate));
180
            return [implode('', array_slice($publicKeyArr, 1, -1)), implode('', array_slice($privateKeyArr, 1, -1))];
181
        }
182
    }
183
184
    /**
185
     * 设置密钥对
186
     *
187
     * - 给定数组必须为只包含两个元素的一维数组,第一个为公钥,第二个为私钥
188
     * - 给 false 则表示不提供该值
189
     *
190
     * @param array $pair 第一项为公钥,第二项为私钥,自动检测是否被压缩
191
     *
192
     * @return static
193
     */
194
    public function setPair($pair)
195
    {
196
        if (2 !== Arrays::count($pair)) {
197
            throw new Exception("请给只包含两个元素的一维数组,第一个为公钥,第二个为私钥。如果不想给,请填 false");
198
        }
199
        if (false !== $pair[0]) {
200
            $this->_pemPublic = $pair[0];
201
            if (false === Strings::isContains($this->_pemPublic, "\n")) {
202
                $this->_pemPublic = "-----BEGIN PUBLIC KEY-----\n" .
203
                chunk_split($this->_pemPublic, 64, "\n") .
204
                    '-----END PUBLIC KEY-----';
205
            }
206
        }
207
        if (false !== $pair[1]) {
208
            $this->_pemPrivate = $pair[1];
209
            if (false === Strings::isContains($this->_pemPrivate, "\n")) {
210
                $this->_pemPrivate = "-----BEGIN PRIVATE KEY-----\n" .
211
                chunk_split($this->_pemPrivate, 64, "\n") .
212
                    '-----END PRIVATE KEY-----';
213
            }
214
        }
215
216
        return $this;
217
    }
218
219
    /**
220
     * 获取被公钥加密的值
221
     *
222
     * - 加密后由于无法正常显示,因此会返回 base64 后的结果
223
     *
224
     * @param string $data
225
     *
226
     * @return string
227
     */
228
    public function getPublicEncrypt($data)
229
    {
230
        if (null === $this->_pemPublic) {
231
            throw new Exception("请使用 setPair 提供公钥");
232
        }
233
        openssl_public_encrypt($data, $encrypted, $this->_pemPublic);
234
        return Base64::encode($encrypted);
235
    }
236
237
    /**
238
     * 获取被私钥加密后,公钥解密的值
239
     *
240
     * @param string $encrypted 被私钥加密后的密文
241
     *
242
     * @return string|boolean
243
     */
244
    public function getPublicDecrypt($encrypted)
245
    {
246
        if (null === $this->_pemPublic) {
247
            throw new Exception("请使用 setPair 提供公钥");
248
        }
249
        $encrypted = Base64::isBase64($encrypted) ? Base64::decode($encrypted) : $encrypted;
250
        if ($encrypted) {
251
            openssl_public_decrypt($encrypted, $decrypted, $this->_pemPublic);
252
            return $decrypted;
253
        }
254
        return false;
255
    }
256
257
    /**
258
     * 获取被私钥加密的值
259
     *
260
     * - 加密后由于无法正常显示,因此会返回 base64 后的结果
261
     *
262
     * @param string $data
263
     *
264
     * @return string
265
     */
266
    public function getPrivateEncrypt($data)
267
    {
268
        if (null === $this->_pemPrivate) {
269
            throw new Exception("请使用 setPair 提供私钥");
270
        }
271
        openssl_private_encrypt($data, $encrypted, $this->_pemPrivate);
272
        return Base64::encode($encrypted);
273
    }
274
275
    /**
276
     * 获取被公钥加密后,私钥解密的值
277
     *
278
     * @param string $encrypted 被公钥加密后的密文
279
     *
280
     * @return string|boolean
281
     */
282
    public function getPrivateDecrypt($encrypted)
283
    {
284
        if (null === $this->_pemPrivate) {
285
            throw new Exception("请使用 setPair 提供私钥");
286
        }
287
        $encrypted = Base64::isBase64($encrypted) ? Base64::decode($encrypted) : $encrypted;
288
        if ($encrypted) {
289
            openssl_private_decrypt($encrypted, $decrypted, $this->_pemPrivate);
290
            return $decrypted;
291
        }
292
        return false;
293
    }
294
295
    /**
296
     * 在证书中使用的专有名称或主题字段
297
     *
298
     * @var array
299
     */
300
    protected $_dn = [];
301
302
    /**
303
     * 设置在证书中使用的专有名称或主题字段
304
     *
305
     * @param string $key
306
     * @param string $value
307
     *
308
     * @return static
309
     */
310
    public function setDn($key, $value)
311
    {
312
        $this->_dn[$key] = $value;
313
        return $this;
314
    }
315
316
    /**
317
     * 国家名
318
     *
319
     * @param string $countryName
320
     *
321
     * @return static
322
     */
323
    public function setDnCountryName($countryName)
324
    {
325
        return $this->setDn('countryName', $countryName);
326
    }
327
328
    /**
329
     * 省份
330
     *
331
     * @param string $stateOrProvinceName
332
     *
333
     * @return static
334
     */
335
    public function setDnStateOrProvinceName($stateOrProvinceName)
336
    {
337
        return $this->setDn('stateOrProvinceName', $stateOrProvinceName);
338
    }
339
340
    /**
341
     * 城市
342
     *
343
     * @param string $localityName
344
     *
345
     * @return static
346
     */
347
    public function setDnLocalityName($localityName)
348
    {
349
        return $this->setDn('localityName', $localityName);
350
    }
351
352
    /**
353
     * 注册人姓名
354
     *
355
     * @param string $organizationName
356
     *
357
     * @return static
358
     */
359
    public function setDnOrganizationName($organizationName)
360
    {
361
        return $this->setDn('organizationName', $organizationName);
362
    }
363
364
    /**
365
     * 组织名称
366
     *
367
     * @param string $organizationalUnitName
368
     *
369
     * @return static
370
     */
371
    public function setDnOrganizationalUnitName($organizationalUnitName)
372
    {
373
        return $this->setDn('organizationalUnitName', $organizationalUnitName);
374
    }
375
376
    /**
377
     * 公共名称
378
     *
379
     * @param string $commonName
380
     *
381
     * @return static
382
     */
383
    public function setDnCommonName($commonName)
384
    {
385
        return $this->setDn('commonName', $commonName);
386
    }
387
388
    /**
389
     * 邮箱
390
     *
391
     * @param string $emailAddress
392
     *
393
     * @return static
394
     */
395
    public function setDnEmailAddress($emailAddress)
396
    {
397
        return $this->setDn('emailAddress', $emailAddress);
398
    }
399
400
    /**
401
     * pem 格式的整数
402
     *
403
     * @var string
404
     */
405
    protected $_pemCert;
406
    /**
407
     * pem 格式 csr(证书请求)
408
     *
409
     * @var string
410
     */
411
    protected $_pemCsr;
412
    /**
413
     * p12 格式的私钥
414
     *
415
     * @var string
416
     */
417
    protected $_p12Private;
418
419
    /**
420
     * 生成证书
421
     *
422
     * - 如若没有设置私钥,则默认生成一对密钥对,用 setPair 设置私钥
423
     *
424
     * @return static
425
     */
426
    public function generateCert()
427
    {
428
        if (null === $this->_pemPrivate) {
429
            $this->generatePair();
430
        }
431
        // 生成一个 CSR 资源
432
        $csr = openssl_csr_new($this->_dn, $this->_pemPrivate, $this->_config);
433
        // 用另一个证书签署 CSR (或者本身) 并且生成一个证书
434
        $x509 = openssl_csr_sign($csr, null, $this->_pemPrivate, 365, $this->_config);
435
        // 将 x509 以PEM编码的格式导出
436
        openssl_x509_export($x509, $this->_pemCert);
437
        openssl_csr_export($csr, $this->_pemCsr);
438
        // 将 PKCS#12 兼容证书存储文件导出到变量
439
        openssl_pkcs12_export($x509, $this->_p12Private, $this->_pemPrivate, $this->_password);
440
        return $this;
441
    }
442
443
    /**
444
     * 返回生成的证书参数
445
     *
446
     * - pemPublic:pem 格式的公钥
447
     * - pemPrivate:pem 格式的私钥
448
     * - pemCert:pem 格式的证书
449
     * - pemCsr:pem 格式的CSR(证书请求)
450
     * - p12Private:p12 格式的私钥
451
     *
452
     * @return array
453
     */
454
    public function getCert()
455
    {
456
        return [
457
            'pemPublic' => $this->_pemPublic,
458
            'pemPrivate' => $this->_pemPrivate,
459
            'pemCert' => $this->_pemCert,
460
            'pemCsr' => $this->_pemCsr,
461
            'p12Private' => $this->_p12Private,
462
        ];
463
    }
464
465
    /**
466
     * 获取用私钥生成的签名
467
     *
468
     * @param string $data 待签名数据
469
     * @param integer $signType 见[openssl_sign](https://www.php.net/manual/zh/function.openssl-sign.php)
470
     *
471
     * @return string
472
     */
473
    public function getSignature($data, $signType = OPENSSL_ALGO_SHA256)
474
    {
475
        if (null === $this->_pemPrivate) {
476
            throw new Exception("请使用 setPair 提供私钥");
477
        }
478
        openssl_sign($data, $signature, $this->_pemPrivate, $signType);
479
        return $signature;
480
    }
481
482
    /**
483
     * 校验签名
484
     *
485
     * @param string $data
486
     * @param string $signature
487
     * @param integer $signType
488
     *
489
     * @return boolean
490
     */
491
    public function isVerify($data, $signature, $signType = OPENSSL_ALGO_SHA256)
492
    {
493
        if (null === $this->_pemPublic) {
494
            throw new Exception("请使用 setPair 提供公钥");
495
        }
496
        return (boolean) openssl_verify($data, $signature, $this->_pemPublic, $signType);
497
    }
498
}
499