CertificateFaker::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
ccs 5
cts 5
cp 1
crap 1
rs 10
c 1
b 0
f 0
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\Support;
26
27
use Derafu\Lib\Core\Package\Prime\Component\Certificate\Exception\CertificateException;
28
use OpenSSLAsymmetricKey;
29
30
/**
31
 * Clase que se encarga de generar certificados autofirmados y retornarlos como
32
 * un string de datos, un arreglo o una instancia de Certificate.
33
 */
34
class CertificateFaker
35
{
36
    /**
37
     * Datos del sujeto del certificado.
38
     *
39
     * @var array
40
     */
41
    private array $subject;
42
43
    /**
44
     * Datos del emisor del certificado.
45
     *
46
     * @var array
47
     */
48
    private array $issuer;
49
50
    /**
51
     * Validez del certificado en formato UNIX timestamp.
52
     *
53
     * @var array
54
     */
55
    private array $validity;
56
57
    /**
58
     * Contraseña para proteger la clave privada en el certificado.
59
     *
60
     * @var string
61
     */
62
    private string $password;
63
64
    /**
65
     * Constructor que asigna por defecto todos los valores por defecto.
66
     */
67 24
    public function __construct()
68
    {
69 24
        $this->setSubject();
70 24
        $this->setIssuer();
71 24
        $this->setValidity();
72 24
        $this->setPassword();
73
    }
74
75
    /**
76
     * Configura los datos del sujeto del certificado.
77
     *
78
     * @param string $C País del sujeto.
79
     * @param string $ST Estado o provincia del sujeto.
80
     * @param string $L Localidad del sujeto.
81
     * @param string $O Organización del sujeto.
82
     * @param string $OU Unidad organizativa del sujeto.
83
     * @param string $CN Nombre común del sujeto.
84
     * @param string $emailAddress Correo electrónico del sujeto.
85
     * @param string $serialNumber Número de serie del sujeto.
86
     * @param string $title Título del sujeto.
87
     * @return static
88
     */
89 24
    public function setSubject(
90
        string $C = 'CL',
91
        string $ST = 'Colchagua',
92
        string $L = 'Santa Cruz',
93
        string $O = 'Organización Intergaláctica de Robots',
94
        string $OU = 'Tecnología',
95
        string $CN = 'Daniel',
96
        string $emailAddress = '[email protected]',
97
        string $serialNumber = '11222333-9',
98
        string $title = 'Bot',
99
    ): static {
100 24
        if (empty($CN) || empty($emailAddress) || empty($serialNumber)) {
101 2
            throw new CertificateException(
102 2
                'El CN, emailAddress y serialNumber son obligatorios.'
103 2
            );
104
        }
105
106 24
        $this->subject = [
107 24
            'C' => $C,
108 24
            'ST' => $ST,
109 24
            'L' => $L,
110 24
            'O' => $O,
111 24
            'OU' => $OU,
112 24
            'CN' => $CN,
113 24
            'emailAddress' => $emailAddress,
114 24
            'serialNumber' => strtoupper($serialNumber),
115 24
            'title' => $title,
116 24
        ];
117
118 24
        return $this;
119
    }
120
121
    /**
122
     * Configura los datos del emisor del certificado.
123
     *
124
     * @param string $C País del emisor.
125
     * @param string $ST Estado o provincia del emisor.
126
     * @param string $L Localidad del emisor.
127
     * @param string $O Organización del emisor.
128
     * @param string $OU Unidad organizativa del emisor.
129
     * @param string $CN Nombre común del emisor.
130
     * @param string $emailAddress Correo electrónico del emisor.
131
     * @param string $serialNumber Número de serie del emisor.
132
     * @return static
133
     */
134 24
    public function setIssuer(
135
        string $C = 'CL',
136
        string $ST = 'Colchagua',
137
        string $L = 'Santa Cruz',
138
        string $O = 'Derafu',
139
        string $OU = 'Tecnología',
140
        string $CN = 'Derafu Autoridad Certificadora de Pruebas',
141
        string $emailAddress = '[email protected]',
142
        string $serialNumber = '76192083-9',
143
    ): static {
144 24
        $this->issuer = [
145 24
            'C' => $C,
146 24
            'ST' => $ST,
147 24
            'L' => $L,
148 24
            'O' => $O,
149 24
            'OU' => $OU,
150 24
            'CN' => $CN,
151 24
            'emailAddress' => $emailAddress,
152 24
            'serialNumber' => strtoupper($serialNumber),
153 24
        ];
154
155 24
        return $this;
156
    }
157
158
    /**
159
     * Configura la validez del certificado.
160
     *
161
     * @param int $days Días que el certificado será válido desde la
162
     * fecha actual. Si no se proporciona, tendrá validez de 365 días.
163
     * @return static
164
     */
165 24
    public function setValidity(int $days = 365): static
166
    {
167 24
        $this->validity = [
168 24
            'days' => $days,
169 24
        ];
170
171 24
        return $this;
172
    }
173
174
    /**
175
     * Configura la contraseña para proteger la clave privada.
176
     *
177
     * @param string $password Contraseña para proteger la clave privada.
178
     * @return void
179
     */
180 24
    public function setPassword(string $password = 'i_love_derafu')
181
    {
182 24
        $this->password = $password;
183
    }
184
185
    /**
186
     * Genera un certificado digital en formato PKCS#12 y lo devuelve como un
187
     * string.
188
     *
189
     * @return string Certificado digital en formato PKCS#12.
190
     */
191 22
    private function toString(): string
192
    {
193
        // Días de validez del certificado (emisor y sujeto).
194 22
        $days = $this->validity['days'];
195
196
        // Crear clave privada y CSR para el emisor.
197 22
        $issuerPrivateKey = openssl_pkey_new();
198 22
        if (!$issuerPrivateKey instanceof OpenSSLAsymmetricKey) {
199
            throw new CertificateException(
200
                'No fue posible generar la llave privada del emisor del certificado.'
201
            );
202
        }
203 22
        $issuerCsr = openssl_csr_new($this->issuer, $issuerPrivateKey);
204
205
        // Crear certificado autofirmado para el emisor (CA).
206 22
        $issuerCert = openssl_csr_sign(
207 22
            $issuerCsr,         // CSR del emisor.
208 22
            null,               // Certificado emisor (null indica que es autofirmado).
209 22
            $issuerPrivateKey,  // Clave privada del emisor.
210 22
            $days,              // Número de días de validez (misma sujeto).
211 22
            [],                 // Opciones adicionales.
212 22
            666                 // Número de serie del certificado.
213 22
        );
214
215
        // Validar que se haya podido crear el certificado del emisor (CA).
216 22
        if ($issuerCert === false) {
217
            throw new CertificateException(
218
                'No fue posible generar el certificado del emisor (CA).'
219
            );
220
        }
221
222
        // Crear clave privada y CSR para el sujeto.
223 22
        $subjectPrivateKey = openssl_pkey_new();
224 22
        if (!$subjectPrivateKey instanceof OpenSSLAsymmetricKey) {
225
            throw new CertificateException(
226
                'No fue posible generar la llave privada del certificado.'
227
            );
228
        }
229 22
        $subjectCsr = openssl_csr_new($this->subject, $subjectPrivateKey);
230
231
        // Usar el certificado del emisor para firmar el CSR del sujeto.
232 22
        $subjectCert = openssl_csr_sign(
233 22
            $subjectCsr,        // La solicitud de firma del certificado (CSR).
234 22
            $issuerCert,        // Certificado emisor.
235 22
            $issuerPrivateKey,  // Clave privada del emisor.
236 22
            $days,              // Número de días de validez.
237 22
            [],                 // Opciones adicionales.
238 22
            69                  // Número de serie del certificado.
239 22
        );
240
241
        // Validar que se haya podido crear el certificado del usuario.
242 22
        if ($subjectCert === false) {
243
            throw new CertificateException(
244
                'No fue posible generar el certificado del usuario.'
245
            );
246
        }
247
248
        // Exportar el certificado final en formato PKCS#12.
249 22
        openssl_pkcs12_export(
250 22
            $subjectCert,
251 22
            $data,
252 22
            $subjectPrivateKey,
253 22
            $this->password
254 22
        );
255
256
        // Entregar los datos del certificado digital.
257 22
        return $data;
258
    }
259
260
    /**
261
     * Genera un certificado digital en formato PKCS#12 y lo devuelve como un
262
     * arreglo.
263
     *
264
     * @return array Certificado digital en formato PKCS#12.
265
     */
266 22
    public function toArray(): array
267
    {
268 22
        $data = $this->toString();
269 22
        $array = [];
270 22
        openssl_pkcs12_read($data, $array, $this->password);
271
272 22
        return $array;
273
    }
274
}
275