Passed
Branch master (0b4ab1)
by Esteban De La Fuente
74:02 queued 50:02
created

FakerWorker   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 237
Duplicated Lines 0 %

Test Coverage

Coverage 87.63%

Importance

Changes 0
Metric Value
eloc 81
c 0
b 0
f 0
dl 0
loc 237
ccs 85
cts 97
cp 0.8763
rs 10
wmc 16

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setSubject() 0 30 4
A createAsString() 0 67 5
A setValidity() 0 7 1
A __construct() 0 8 1
A create() 0 5 1
A createAsArray() 0 7 1
A getPassword() 0 3 1
A setIssuer() 0 22 1
A setPassword() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Derafu: Biblioteca PHP (Núcleo).
7
 * Copyright (C) Derafu <https://www.derafu.org>
8
 *
9
 * Este programa es software libre: usted puede redistribuirlo y/o modificarlo
10
 * bajo los términos de la Licencia Pública General Affero de GNU publicada por
11
 * la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o
12
 * (a su elección) cualquier versión posterior de la misma.
13
 *
14
 * Este programa se distribuye con la esperanza de que sea útil, pero SIN
15
 * GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD
16
 * PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública
17
 * General Affero de GNU para obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de GNU
20
 * junto a este programa.
21
 *
22
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
23
 */
24
25
namespace Derafu\Lib\Core\Package\Prime\Component\Certificate\Worker;
26
27
use Derafu\Lib\Core\Foundation\Abstract\AbstractWorker;
28
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Contract\FakerWorkerInterface;
29
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Contract\LoaderWorkerInterface;
30
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Entity\Certificate;
31
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Exception\CertificateException;
32
use OpenSSLAsymmetricKey;
33
34
/**
35
 * Clase que se encarga de generar certificados autofirmados y retornarlos como
36
 * un string de datos, un arreglo o una instancia de Certificate.
37
 */
38
class FakerWorker extends AbstractWorker implements FakerWorkerInterface
39
{
40
    /**
41
     * Datos del sujeto del certificado.
42
     *
43
     * @var array
44
     */
45
    private array $subject;
46
47
    /**
48
     * Datos del emisor del certificado.
49
     *
50
     * @var array
51
     */
52
    private array $issuer;
53
54
    /**
55
     * Validez del certificado en formato UNIX timestamp.
56
     *
57
     * @var array
58
     */
59
    private array $validity;
60
61
    /**
62
     * Contraseña para proteger la clave privada en el certificado.
63
     *
64
     * @var string
65
     */
66
    private string $password;
67
68
    /**
69
     * Instancia que permite cargar (crear) certificados a partir de sus datos.
70
     *
71
     * @var LoaderWorkerInterface
72
     */
73
    private LoaderWorkerInterface $loader;
74
75
    /**
76
     * Constructor de la clase.
77
     *
78
     * Establece valores por defecto para el sujeto, emisor, validez y
79
     * contraseña.
80
     *
81
     * @param LoaderWorkerInterface $loader
82
     */
83 26
    public function __construct(LoaderWorkerInterface $loader)
84
    {
85 26
        $this->loader = $loader;
86
87 26
        $this->setSubject();
88 26
        $this->setIssuer();
89 26
        $this->setValidity();
90 26
        $this->setPassword();
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 26
    public function setSubject(
97
        string $C = 'CL',
98
        string $ST = 'Colchagua',
99
        string $L = 'Santa Cruz',
100
        string $O = 'Organización Intergaláctica de Robots',
101
        string $OU = 'Tecnología',
102
        string $CN = 'Daniel',
103
        string $emailAddress = '[email protected]',
104
        string $serialNumber = '11222333-9',
105
        string $title = 'Bot',
106
    ): static {
107 26
        if (empty($CN) || empty($emailAddress) || empty($serialNumber)) {
108 2
            throw new CertificateException(
109 2
                'El CN, emailAddress y serialNumber son obligatorios.'
110 2
            );
111
        }
112
113 26
        $this->subject = [
114 26
            'C' => $C,
115 26
            'ST' => $ST,
116 26
            'L' => $L,
117 26
            'O' => $O,
118 26
            'OU' => $OU,
119 26
            'CN' => $CN,
120 26
            'emailAddress' => $emailAddress,
121 26
            'serialNumber' => strtoupper($serialNumber),
122 26
            'title' => $title,
123 26
        ];
124
125 26
        return $this;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 26
    public function setIssuer(
132
        string $C = 'CL',
133
        string $ST = 'Colchagua',
134
        string $L = 'Santa Cruz',
135
        string $O = 'Derafu',
136
        string $OU = 'Tecnología',
137
        string $CN = 'Derafu Autoridad Certificadora de Pruebas',
138
        string $emailAddress = '[email protected]',
139
        string $serialNumber = '76192083-9',
140
    ): static {
141 26
        $this->issuer = [
142 26
            'C' => $C,
143 26
            'ST' => $ST,
144 26
            'L' => $L,
145 26
            'O' => $O,
146 26
            'OU' => $OU,
147 26
            'CN' => $CN,
148 26
            'emailAddress' => $emailAddress,
149 26
            'serialNumber' => strtoupper($serialNumber),
150 26
        ];
151
152 26
        return $this;
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 26
    public function setValidity(int $days = 365): static
159
    {
160 26
        $this->validity = [
161 26
            'days' => $days,
162 26
        ];
163
164 26
        return $this;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170 26
    public function setPassword(string $password = 'i_love_derafu')
171
    {
172 26
        $this->password = $password;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178 2
    public function getPassword(): string
179
    {
180 2
        return $this->password;
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 22
    public function createAsString(): string
187
    {
188
        // Días de validez del certificado (emisor y sujeto).
189 22
        $days = $this->validity['days'];
190
191
        // Crear clave privada y CSR para el emisor.
192 22
        $issuerPrivateKey = openssl_pkey_new();
193 22
        if (!$issuerPrivateKey instanceof OpenSSLAsymmetricKey) {
194
            throw new CertificateException(
195
                'No fue posible generar la llave privada del emisor del certificado.'
196
            );
197
        }
198 22
        $issuerCsr = openssl_csr_new($this->issuer, $issuerPrivateKey);
199
200
        // Crear certificado autofirmado para el emisor (CA).
201 22
        $issuerCert = openssl_csr_sign(
202 22
            $issuerCsr,         // CSR del emisor.
203 22
            null,               // Certificado emisor (null indica que es autofirmado).
204 22
            $issuerPrivateKey,  // Clave privada del emisor.
205 22
            $days,              // Número de días de validez (misma sujeto).
206 22
            [],                 // Opciones adicionales.
207 22
            666                 // Número de serie del certificado.
208 22
        );
209
210
        // Validar que se haya podido crear el certificado del emisor (CA).
211 22
        if ($issuerCert === false) {
212
            throw new CertificateException(
213
                'No fue posible generar el certificado del emisor (CA).'
214
            );
215
        }
216
217
        // Crear clave privada y CSR para el sujeto.
218 22
        $subjectPrivateKey = openssl_pkey_new();
219 22
        if (!$subjectPrivateKey instanceof OpenSSLAsymmetricKey) {
220
            throw new CertificateException(
221
                'No fue posible generar la llave privada del certificado.'
222
            );
223
        }
224 22
        $subjectCsr = openssl_csr_new($this->subject, $subjectPrivateKey);
225
226
        // Usar el certificado del emisor para firmar el CSR del sujeto.
227 22
        $subjectCert = openssl_csr_sign(
228 22
            $subjectCsr,        // La solicitud de firma del certificado (CSR).
229 22
            $issuerCert,        // Certificado emisor.
230 22
            $issuerPrivateKey,  // Clave privada del emisor.
231 22
            $days,              // Número de días de validez.
232 22
            [],                 // Opciones adicionales.
233 22
            69                  // Número de serie del certificado.
234 22
        );
235
236
        // Validar que se haya podido crear el certificado del usuario.
237 22
        if ($subjectCert === false) {
238
            throw new CertificateException(
239
                'No fue posible generar el certificado del usuario.'
240
            );
241
        }
242
243
        // Exportar el certificado final en formato PKCS#12.
244 22
        openssl_pkcs12_export(
245 22
            $subjectCert,
246 22
            $data,
247 22
            $subjectPrivateKey,
248 22
            $this->password
249 22
        );
250
251
        // Entregar los datos del certificado digital.
252 22
        return $data;
253
    }
254
255
    /**
256
     * {@inheritdoc}
257
     */
258 20
    public function createAsArray(): array
259
    {
260 20
        $data = $this->createAsString();
261 20
        $array = [];
262 20
        openssl_pkcs12_read($data, $array, $this->password);
263
264 20
        return $array;
265
    }
266
267
    /**
268
     * {@inheritdoc}
269
     */
270 17
    public function create(): Certificate
271
    {
272 17
        $array = $this->createAsArray();
273
274 17
        return $this->loader->createFromArray($array);
275
    }
276
}
277