Passed
Push — master ( af6f81...caf93d )
by Keoghan
04:44 queued 11s
created

CertificateBuilder::createCertificate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 2.0008

Importance

Changes 0
Metric Value
eloc 16
c 0
b 0
f 0
dl 0
loc 22
ccs 16
cts 17
cp 0.9412
rs 9.7333
cc 2
nc 2
nop 1
crap 2.0008
1
<?php
2
3
namespace App\Support\Ssl;
4
5
use App\Support\Contracts\Cli;
6
use App\Support\Mechanics\Mechanic;
7
use Illuminate\Filesystem\Filesystem;
8
use Symfony\Component\Finder\SplFileInfo;
9
10
class CertificateBuilder
11
{
12
    protected $cli;
13
    protected $filesystem;
14
    protected $certificatesPath;
15
    protected $oName;
16
    protected $cName;
17
    protected $domain;
18
    protected $email;
19
20
    /**
21
     * Driver that will interact with the operating system.
22
     *
23
     * @var Mechanic
24
     */
25
    protected $mechanic;
26
27 12
    public function __construct(Cli $cli, Filesystem $filesystem, Mechanic $mechanic, $certificatesPath)
28
    {
29 12
        $this->cli = $cli;
30 12
        $this->filesystem = $filesystem;
31 12
        $this->mechanic = $mechanic;
32 12
        $this->certificatesPath = $certificatesPath;
33
34 12
        $this->oName = 'Klever Porter CA Self Signed Organization';
35 12
        $this->cName = 'Klever Porter CA Self Signed CN';
36 12
        $this->domain = 'klever.porter';
37 12
        $this->email = 'rootcertificate@'.$this->domain;
38 12
    }
39
40
    /**
41
     * List the certificate authority paths.
42
     *
43
     * @return object
44
     */
45 6
    public function caPaths()
46
    {
47
        return (object) [
48 6
            'key' => $this->certificatesPath.'/KleverPorterCASelfSigned.key',
49 6
            'pem' => $this->certificatesPath.'/KleverPorterCASelfSigned.pem',
50 6
            'srl' => $this->certificatesPath.'/KleverPorterCASelfSigned.srl',
51
        ];
52
    }
53
54
    /**
55
     * List paths based on the certificate url.
56
     *
57
     * @param $url
58
     *
59
     * @return object
60
     */
61 5
    public function paths($url)
62
    {
63
        return (object) [
64 5
            'key'  => $this->certificatesPath.'/'.$url.'.key',
65 5
            'csr'  => $this->certificatesPath.'/'.$url.'.csr',
66 5
            'crt'  => $this->certificatesPath.'/'.$url.'.crt',
67 5
            'conf' => $this->certificatesPath.'/'.$url.'.conf',
68
        ];
69
    }
70
71
    /**
72
     * Build a certificate based on the url.  Create CA if needed.
73
     *
74
     * @param $url
75
     */
76 4
    public function build($url)
77
    {
78 4
        $this->destroy($url);
79 4
        $this->createCa();
80 4
        $this->createCertificate($url);
81 4
    }
82
83
    /**
84
     * Destroy certificate for site based on url.
85
     *
86
     * @param $url
87
     */
88 5
    public function destroy($url)
89
    {
90 5
        foreach ($this->paths($url) as $path) {
91 5
            $this->filesystem->delete($path);
92
        }
93 5
    }
94
95
    /**
96
     * Create certificate authority.
97
     */
98 4
    public function createCa()
99
    {
100 4
        $paths = $this->caPaths();
101
102 4
        if ($this->filesystem->exists($paths->key) || $this->filesystem->exists($paths->pem)) {
103
            return;
104
        }
105
106 4
        $this->cli->exec(sprintf(
107 4
            'openssl req -new -newkey rsa:2048 -days 730 -nodes -x509 -subj "/C=GB/ST=Berks/O=%s/localityName=Reading/commonName=%s/organizationalUnitName=Developers/emailAddress=%s/" -keyout %s -out %s',
108 4
            $this->oName,
109 4
            $this->cName,
110 4
            $this->email,
111 4
            $paths->key,
112 4
            $paths->pem
113
        ));
114
115 4
        $this->mechanic->trustCA($paths->pem);
116 4
    }
117
118
    /**
119
     * Create a certificate for the given URL.
120
     *
121
     * @param string $url
122
     *
123
     * @return void
124
     */
125 4
    public function createCertificate($url)
126
    {
127 4
        $paths = $this->paths($url);
128 4
        $caPaths = $this->caPaths();
129
130 4
        $this->createConf($paths->conf, $url);
131 4
        $this->createPrivateKey($paths->key);
132 4
        $this->createSigningRequest($url, $paths->key, $paths->csr, $paths->conf);
133
134 4
        $caSrlParam = ' -CAcreateserial';
135 4
        if ($this->filesystem->exists($caPaths->srl)) {
136
            $caSrlParam = ' -CAserial '.$caPaths->srl;
137
        }
138
139 4
        $this->cli->exec(sprintf(
140 4
            'openssl x509 -req -sha256 -days 730 -CA %s -CAkey %s%s -in %s -out %s -extensions v3_req -extfile %s',
141 4
            $caPaths->pem,
142 4
            $caPaths->key,
143 4
            $caSrlParam,
144 4
            $paths->csr,
145 4
            $paths->crt,
146 4
            $paths->conf
147
        ));
148
149
        // Trusting the certificate shouldn't be necessary once the CA is trusted.
150
        // $this->mechanic->trustCertificate($paths->crt);
151 4
    }
152
153
    /**
154
     * Build the SSL config for the given URL.
155
     *
156
     * @param $path
157
     * @param $url
158
     */
159 4
    public function createConf($path, $url)
160
    {
161 4
        $this->filesystem->put($path, view('ssl.conf')->withUrl($url)->render());
162 4
    }
163
164
    /**
165
     * Create the private key for the TLS certificate.
166
     *
167
     * @param string $keyPath
168
     *
169
     * @return void
170
     */
171 4
    public function createPrivateKey($keyPath)
172
    {
173 4
        $this->cli->exec(sprintf('openssl genrsa -out %s 2048', $keyPath));
174 4
    }
175
176
    /**
177
     * Create the signing request for the TLS certificate.
178
     *
179
     * @param        $url
180
     * @param string $keyPath
181
     * @param        $csrPath
182
     * @param        $confPath
183
     *
184
     * @return void
185
     */
186 4
    public function createSigningRequest($url, $keyPath, $csrPath, $confPath)
187
    {
188 4
        $this->cli->exec(sprintf(
189 4
            'openssl req -new -key %s -out %s -subj "/C=GB/ST=Berks/O=%s/localityName=Reading/commonName=%s/organizationalUnitName=Developers/emailAddress=%s%s/" -config %s',
190 4
            $keyPath,
191 4
            $csrPath,
192 4
            $this->domain,
193 4
            $url,
194 4
            $url,
195 4
            '@'.$this->domain,
196 4
            $confPath
197
        ));
198 4
    }
199
200
    /**
201
     * Clear generated certs. Optionally clear CA too.
202
     *
203
     * @param bool $dropCA
204
     */
205 2
    public function clearCertificates($dropCA = false)
206
    {
207 2
        $caPaths = (array) $this->caPaths();
208
209 2
        $files = $this->filesystem
210 2
            ->allFiles($this->certificatesPath);
211
212 2
        foreach ($files as $file) {
213
            /** @var SplFileInfo $file */
214 2
            if (!$dropCA && in_array($file->getPathname(), $caPaths)) {
215 1
                continue;
216
            }
217
218 2
            $this->filesystem->delete($file->getPathname());
219
        }
220 2
    }
221
}
222