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 ( 24ae15...d40ff0 )
by t
05:40 queued 03:33
created

Crypto   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 464
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 108
c 3
b 1
f 0
dl 0
loc 464
ccs 122
cts 122
cp 1
rs 9.1199
wmc 41

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setDn() 0 4 1
A getPrivateDecrypt() 0 9 3
A __construct() 0 8 1
A getPublicDecrypt() 0 9 3
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 6 1
A getPair() 0 11 4
A getPublicEncrypt() 0 7 2
A setConfig() 0 6 2
A isVerify() 0 4 1
A setPassword() 0 4 1
A setPair() 0 21 5
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 8 1

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