Passed
Push — master ( a8bdfa...0cad3e )
by Esteban De La Fuente
09:10
created

CertificateFaker::setSubject()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
c 1
b 0
f 0
nc 2
nop 9
dl 0
loc 30
ccs 17
cts 17
cp 1
crap 4
rs 9.7998

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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