Passed
Push — develop ( 08b6c2...caa568 )
by Nikita
05:33
created

CertificateService::signCsr()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 56
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 36
nc 18
nop 1
dl 0
loc 56
rs 8.4106
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Gameap\Services;
4
5
use Gameap\Exceptions\GameapException;
6
use Illuminate\Support\Facades\Storage;
7
use Carbon\Carbon;
8
use phpseclib\Crypt\RSA;
9
use phpseclib\File\X509;
10
11
class CertificateService
12
{
13
    const ROOT_CA = 'certs/root.crt';
14
    const ROOT_KEY = 'certs/root.key';
15
16
//    const CERT_DN = [
17
//        "countryName" => "RU",
18
//        "stateOrProvinceName" => "GameAP",
19
//        "localityName" => "GameAP",
20
//        "organizationName" => "GameAP.ru",
21
//        "organizationalUnitName" => "Development",
22
//        "commonName" => "GameAP",
23
//        "emailAddress" => "[email protected]"
24
//    ];
25
26
    const CERT_DN = '/CN=GameAP/O=GameAP';
27
28
    const CERT_DAYS = 3650;
29
30
    /**
31
     * Generate root certificate
32
     */
33
    static public function generateRoot()
34
    {
35
        $privKey = new RSA();
36
        $keys = $privKey->createKey(2048);
37
        $privKey->loadKey($keys['privatekey']);
38
39
        $pubKey = new RSA();
40
        $pubKey->loadKey($keys['publickey']);
41
        $pubKey->setPublicKey();
42
43
        $subject = new X509();
44
        $subject->setDN(self::CERT_DN);
45
        $subject->setPublicKey($pubKey);
46
47
        $issuer = new X509();
48
        $issuer->setPrivateKey($privKey);
49
        $issuer->setDN($subject->getDN());
50
51
        $x509 = new X509();
52
        $x509->setEndDate('+10 year');
53
        $x509->makeCA();
54
55
        $result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption');
56
57
        if ($result == false) {
0 ignored issues
show
introduced by
The condition $result == false is always true.
Loading history...
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
58
            throw new GameapException('CA Self-signing has failed.');
59
        }
60
61
        Storage::put(self::ROOT_CA, $x509->saveX509($result));
62
        Storage::put(self::ROOT_KEY, $privKey->getPrivateKey());
63
    }
64
65
    /**
66
     * @param $certificatePath string   path to certificate in storage
67
     * @param $keyPath string   path to key in storage
68
     */
69
    static public function generate($certificatePath, $keyPath)
70
    {
71
        if (!Storage::exists(self::ROOT_CA)) {
72
            self::generateRoot();
73
        }
74
75
        $privKey = new RSA();
76
        $keys = $privKey->createKey(2048);
77
        $privKey->loadKey($keys['privatekey']);
78
79
        $pubKey = new RSA();
80
        $pubKey->loadKey($keys['publickey']);
81
        $pubKey->setPublicKey();
82
83
        // Sign
84
        $CAPrivKey = new RSA();
85
        if (! $CAPrivKey->loadKey(Storage::get(self::ROOT_KEY))) {
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...ge::get(self::ROOT_KEY) of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type array|phpseclib\Crypt\RSA|string expected by parameter $key of phpseclib\Crypt\RSA::loadKey(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

85
        if (! $CAPrivKey->loadKey(/** @scrutinizer ignore-type */ Storage::get(self::ROOT_KEY))) {
Loading history...
86
            throw new GameapException('Failed to load CA Private Key');
87
        }
88
89
        $issuer = new X509();
90
        $issuer->loadCA(Storage::get(self::ROOT_CA));
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...age::get(self::ROOT_CA) of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type string expected by parameter $cert of phpseclib\File\X509::loadCA(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

90
        $issuer->loadCA(/** @scrutinizer ignore-type */ Storage::get(self::ROOT_CA));
Loading history...
91
        $issuer->loadX509(Storage::get(self::ROOT_CA));
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...age::get(self::ROOT_CA) of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type string expected by parameter $cert of phpseclib\File\X509::loadX509(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
        $issuer->loadX509(/** @scrutinizer ignore-type */ Storage::get(self::ROOT_CA));
Loading history...
92
        // $issuer->setDN($CASubject);
93
        $issuer->setPrivateKey($CAPrivKey);
94
95
        $subject = new X509();
96
        $subject->setDN(self::CERT_DN);
97
        $subject->setPublicKey($pubKey);
98
99
        $x509 = new X509();
100
        $x509->setEndDate('+10 year');
101
102
        $result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption');
103
        $result['tbsCertificate']['version'] = 'v1';
104
105
        if ($result == false) {
0 ignored issues
show
introduced by
The condition $result == false is always true.
Loading history...
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
106
            throw new GameapException('Signing has failed.');
107
        }
108
109
        Storage::put($certificatePath, $x509->saveX509($result));
110
        Storage::put($keyPath, $privKey->getPrivateKey());
111
    }
112
113
    /**
114
     * @param $csrPath
115
     *
116
     * @return string
117
     * @throws GameapException
118
     */
119
    static public function signCsr($csrPath)
120
    {
121
        if (!Storage::exists(self::ROOT_CA)) {
122
            self::generateRoot();
123
        }
124
125
        $rootCa = Storage::get(self::ROOT_CA);
126
        $rootKey = Storage::get(self::ROOT_KEY);
127
128
        if (file_exists($csrPath)) {
129
            $csr = file_get_contents($csrPath);
130
            $fromStorage = false;
131
        } else if (Storage::exists($csrPath)) {
132
            $csr = Storage::get($csrPath);
133
            $fromStorage = true;
134
        } else {
135
            throw new GameapException('Invalid csr path');
136
        }
137
138
        $pathinfo = pathinfo($csrPath);
139
        $signedCertificatePath = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '.crt';
140
141
        $CAPrivKey = new RSA();
142
        $CAPrivKey->loadKey($rootKey);
0 ignored issues
show
Bug introduced by
$rootKey of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type array|phpseclib\Crypt\RSA|string expected by parameter $key of phpseclib\Crypt\RSA::loadKey(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

142
        $CAPrivKey->loadKey(/** @scrutinizer ignore-type */ $rootKey);
Loading history...
143
144
        $issuer = new X509();
145
        $issuer->loadCA($rootCa);
0 ignored issues
show
Bug introduced by
$rootCa of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type string expected by parameter $cert of phpseclib\File\X509::loadCA(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
        $issuer->loadCA(/** @scrutinizer ignore-type */ $rootCa);
Loading history...
146
        $issuer->loadX509($rootCa);
0 ignored issues
show
Bug introduced by
$rootCa of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type string expected by parameter $cert of phpseclib\File\X509::loadX509(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
        $issuer->loadX509(/** @scrutinizer ignore-type */ $rootCa);
Loading history...
147
        $issuer->setPrivateKey($CAPrivKey);
148
149
        $subject = new X509();
150
        $subject->loadCSR($csr);
0 ignored issues
show
Bug introduced by
It seems like $csr can also be of type Illuminate\Contracts\Filesystem\Filesystem; however, parameter $csr of phpseclib\File\X509::loadCSR() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

150
        $subject->loadCSR(/** @scrutinizer ignore-type */ $csr);
Loading history...
151
152
        $x509 = new X509();
153
        $x509->setEndDate('+10 year');
154
155
        $result = $x509->sign($issuer, $subject, 'sha256WithRSAEncryption');
156
        $result['tbsCertificate']['version'] = 'v1';
157
158
        if ($result == false) {
0 ignored issues
show
introduced by
The condition $result == false is always true.
Loading history...
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
159
            throw new GameapException('Signing has failed.');
160
        }
161
162
        $pemCertificate = $x509->saveX509($result);
163
164
        if (empty($pemCertificate)) {
165
            throw new GameapException('X509 Saving has failed.');
166
        }
167
168
        if ($fromStorage) {
169
            Storage::put($signedCertificatePath, $pemCertificate);
170
        } else {
171
            file_put_contents($signedCertificatePath, $pemCertificate);
172
        }
173
174
        return $signedCertificatePath;
175
    }
176
177
    /**
178
     * @param $certificatePath
179
     *
180
     * @return string
181
     */
182
    static public function fingerprintString($certificatePath)
183
    {
184
        $fingerprint = openssl_x509_fingerprint(Storage::get($certificatePath), 'sha256');
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...::get($certificatePath) of type Illuminate\Contracts\Filesystem\Filesystem is incompatible with the type string expected by parameter $x509 of openssl_x509_fingerprint(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

184
        $fingerprint = openssl_x509_fingerprint(/** @scrutinizer ignore-type */ Storage::get($certificatePath), 'sha256');
Loading history...
185
        return strtoupper(implode(':', str_split($fingerprint, 2)));
186
    }
187
188
    /**
189
     * @param $certificatePath
190
     *
191
     * @return array
192
     */
193
    static public function certificateInfo($certificatePath)
194
    {
195
        $parsed = openssl_x509_parse(Storage::get($certificatePath));
196
197
        return [
198
            'expires' => Carbon::createFromTimestamp($parsed['validTo_time_t'])->toDateTimeString(),
199
200
            'signature_type' => $parsed['signatureTypeSN'],
201
202
            'country' => $parsed['subject']['C'] ?? '',
203
            'state' => $parsed['subject']['ST'] ?? '',
204
            'locality' => $parsed['subject']['L'] ?? '',
205
            'organization' => $parsed['subject']['O'] ?? '',
206
            'organizational_unit' => $parsed['subject']['OU'] ?? '',
207
            'common_name' => $parsed['subject']['CN'] ?? '',
208
            'email' => $parsed['subject']['emailAddress'] ?? '',
209
210
            'issuer_country' => $parsed['issuer']['C'] ?? '',
211
            'issuer_state' => $parsed['issuer']['ST'] ?? '',
212
            'issuer_locality' => $parsed['issuer']['L'] ?? '',
213
            'issuer_organization' => $parsed['issuer']['O'] ?? '',
214
            'issuer_organizational_unit' => $parsed['issuer']['OU'] ?? '',
215
            'issuer_common_name' => $parsed['issuer']['CN'] ?? '',
216
            'issuer_email' => $parsed['issuer']['emailAddress'] ?? '',
217
        ];
218
    }
219
}