Passed
Push — master ( ee18aa...2d6897 )
by Keoghan
03:19
created

CertificateBuilder::clearCertificates()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

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