Completed
Push — master ( c12f88...3d35d7 )
by Esteban De La Fuente
01:59
created

FirmaElectronica::getExpirationDays()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/**
4
 * LibreDTE
5
 * Copyright (C) SASCO SpA (https://sasco.cl)
6
 *
7
 * Este programa es software libre: usted puede redistribuirlo y/o
8
 * modificarlo bajo los términos de la Licencia Pública General Affero de GNU
9
 * publicada por la Fundación para el Software Libre, ya sea la versión
10
 * 3 de la Licencia, o (a su elección) cualquier versión posterior de la
11
 * misma.
12
 *
13
 * Este programa se distribuye con la esperanza de que sea útil, pero
14
 * SIN GARANTÍA ALGUNA; ni siquiera la garantía implícita
15
 * MERCANTIL o de APTITUD PARA UN PROPÓSITO DETERMINADO.
16
 * Consulte los detalles de la Licencia Pública General Affero de GNU para
17
 * 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
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
22
 */
23
24
namespace sasco\LibreDTE;
25
26
/**
27
 * Clase para trabajar con firma electrónica, permite firmar y verificar firmas.
28
 * Provee los métodos: sign(), verify(), signXML() y verifyXML()
29
 * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
30
 * @version 2019-02-12
31
 */
32
class FirmaElectronica
33
{
34
35
    private $config; ///< Configuración de la firma electrónica
36
    private $certs; ///< Certificados digitales de la firma
37
    private $data; ///< Datos del certificado digial
38
39
    /**
40
     * Constructor para la clase: crea configuración y carga certificado digital
41
     *
42
     * Si se desea pasar una configuración específica para la firma electrónica
43
     * se debe hacer a través de un arreglo con los índices file y pass, donde
44
     * file es la ruta hacia el archivo .p12 que contiene tanto la clave privada
45
     * como la pública y pass es la contraseña para abrir dicho archivo.
46
     * Ejemplo:
47
     *
48
     * \code{.php}
49
     *   $firma_config = ['file'=>'/ruta/al/certificado.p12', 'pass'=>'contraseña'];
50
     *   $firma = new \sasco\LibreDTE\FirmaElectronica($firma_config);
51
     * \endcode
52
     *
53
     * También se permite que en vez de pasar la ruta al certificado p12 se pase
54
     * el contenido del certificado, esto servirá por ejemplo si los datos del
55
     * archivo están almacenados en una base de datos. Ejemplo:
56
     *
57
     * \code{.php}
58
     *   $firma_config = ['data'=>file_get_contents('/ruta/al/certificado.p12'), 'pass'=>'contraseña'];
59
     *   $firma = new \sasco\LibreDTE\FirmaElectronica($firma_config);
60
     * \endcode
61
     *
62
     * @param config Configuración para la clase, si no se especifica se tratará de determinar
63
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
64
     * @version 2015-09-15
65
     */
66
    public function __construct(array $config = [])
67
    {
68
        // crear configuración
69
        if (!$config) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
70
            if (class_exists('\sowerphp\core\Configure')) {
71
                $config = (array)\sowerphp\core\Configure::read('firma_electronica.default');
72
            } else {
73
                $config = [];
74
            }
75
        }
76
        $this->config = array_merge([
77
            'file' => null,
78
            'pass' => null,
79
            'data' => null,
80
            'wordwrap' => 64,
81
        ], $config);
82
        // cargar firma electrónica desde el contenido del archivo .p12 si no
83
        // se pasaron como datos del arreglo de configuración
84
        if (!$this->config['data'] and $this->config['file']) {
85
            if (is_readable($this->config['file'])) {
86
                $this->config['data'] = file_get_contents($this->config['file']);
87
            } else {
88
                return $this->error('Archivo de la firma electrónica '.basename($this->config['file']).' no puede ser leído');
0 ignored issues
show
Documentation introduced by
'Archivo de la firma ele... ' no puede ser leído' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
89
            }
90
        }
91
        // leer datos de la firma electrónica
92
        if ($this->config['data'] and openssl_pkcs12_read($this->config['data'], $this->certs, $this->config['pass'])===false) {
93
            return $this->error('No fue posible leer los datos de la firma electrónica (verificar la contraseña)');
0 ignored issues
show
Documentation introduced by
'No fue posible leer los...ificar la contraseña)' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
94
        }
95
        $this->data = openssl_x509_parse($this->certs['cert']);
96
        // quitar datos del contenido del archivo de la firma
97
        unset($this->config['data']);
98
    }
99
100
    /**
101
     * Método para generar un error usando una excepción de SowerPHP o terminar
102
     * el script si no se está usando el framework
103
     * @param msg Mensaje del error
104
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
105
     * @version 2017-08-04
106
     */
107
    private function error($msg)
108
    {
109
        if (class_exists('\sasco\LibreDTE\Estado') and class_exists('\sasco\LibreDTE\Log')) {
110
            $msg = \sasco\LibreDTE\Estado::get(\sasco\LibreDTE\Estado::FIRMA_ERROR, $msg);
111
            \sasco\LibreDTE\Log::write(\sasco\LibreDTE\Estado::FIRMA_ERROR, $msg);
0 ignored issues
show
Documentation introduced by
$msg is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
112
            return false;
113
        } else {
114
            throw new \Exception($msg);
115
        }
116
    }
117
118
    /**
119
     * Método que agrega el inicio y fin de un certificado (clave pública)
120
     * @param cert Certificado que se desea normalizar
121
     * @return Certificado con el inicio y fin correspondiente
122
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
123
     * @version 2015-08-20
124
     */
125
    private function normalizeCert($cert)
126
    {
127
        if (strpos($cert, '-----BEGIN CERTIFICATE-----')===false) {
128
            $body = trim($cert);
129
            $cert = '-----BEGIN CERTIFICATE-----'."\n";
130
            $cert .= wordwrap($body, $this->config['wordwrap'], "\n", true)."\n";
131
            $cert .= '-----END CERTIFICATE-----'."\n";
132
        }
133
        return $cert;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $cert; of type string|sasco\LibreDTE\Certificado is incompatible with the return type documented by sasco\LibreDTE\FirmaElectronica::normalizeCert of type sasco\LibreDTE\Certificado as it can also be of type string which is not included in this return type.
Loading history...
134
    }
135
136
    /**
137
     * Método que entrega el RUN/RUT asociado al certificado
138
     * @return RUN/RUT asociado al certificado en formato: 11222333-4
0 ignored issues
show
Documentation introduced by
The doc-type RUN/RUT could not be parsed: Unknown type name "RUN/RUT" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
139
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
140
     * @version 2016-02-12
141
     */
142
    public function getID()
143
    {
144
        // RUN/RUT se encuentra en la extensión del certificado, esto de acuerdo
145
        // a Ley 19.799 sobre documentos electrónicos y firma electrónica
146
        $x509 = new \phpseclib\File\X509();
147
        $cert = $x509->loadX509($this->certs['cert']);
148
        if (isset($cert['tbsCertificate']['extensions'])) {
149
            foreach ($cert['tbsCertificate']['extensions'] as $e) {
150
                if ($e['extnId']=='id-ce-subjectAltName') {
151
                    return ltrim($e['extnValue'][0]['otherName']['value']['ia5String'], '0');
152
                }
153
            }
154
        }
155
        // se obtiene desde serialNumber (esto es sólo para que funcione la firma para tests)
156
        if (isset($this->data['subject']['serialNumber'])) {
157
            return ltrim($this->data['subject']['serialNumber'], '0');
158
        }
159
        // no se encontró el RUN
160
        return $this->error('No fue posible obtener el ID de la firma');
0 ignored issues
show
Documentation introduced by
'No fue posible obtener el ID de la firma' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
161
    }
162
163
    /**
164
     * Método que entrega el CN del subject
165
     * @return CN del subject
166
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
167
     * @version 2016-02-12
168
     */
169
    public function getName()
170
    {
171
        if (isset($this->data['subject']['CN']))
172
            return $this->data['subject']['CN'];
173
        return $this->error('No fue posible obtener el Name (subject.CN) de la firma');
0 ignored issues
show
Documentation introduced by
'No fue posible obtener ...ubject.CN) de la firma' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The return type of return $this->error('No ...ject.CN) de la firma'); (boolean) is incompatible with the return type documented by sasco\LibreDTE\FirmaElectronica::getName of type sasco\LibreDTE\CN.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
174
    }
175
176
    /**
177
     * Método que entrega el emailAddress del subject
178
     * @return emailAddress del subject
179
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
180
     * @version 2016-02-12
181
     */
182
    public function getEmail()
183
    {
184
        if (isset($this->data['subject']['emailAddress'])) {
185
            return $this->data['subject']['emailAddress'];
186
        }
187
        return $this->error('No fue posible obtener el Email (subject.emailAddress) de la firma');
0 ignored issues
show
Documentation introduced by
'No fue posible obtener ...ilAddress) de la firma' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The return type of return $this->error('No ...Address) de la firma'); (boolean) is incompatible with the return type documented by sasco\LibreDTE\FirmaElectronica::getEmail of type sasco\LibreDTE\emailAddress.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
188
    }
189
190
    /**
191
     * Método que entrega desde cuando es válida la firma
192
     * @return validFrom_time_t
193
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
194
     * @version 2015-09-22
195
     */
196
    public function getFrom()
197
    {
198
        return date('Y-m-d H:i:s', $this->data['validFrom_time_t']);
199
    }
200
201
    /**
202
     * Método que entrega hasta cuando es válida la firma
203
     * @return validTo_time_t
204
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
205
     * @version 2015-09-22
206
     */
207
    public function getTo()
208
    {
209
        return date('Y-m-d H:i:s', $this->data['validTo_time_t']);
210
    }
211
212
    /**
213
     * Método que entrega los días totales que la firma es válida
214
     * @return int Días totales en que la firma es válida
215
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
216
     * @version 2019-02-12
217
     */
218
    public function getTotalDays()
219
    {
220
        $start = new \DateTime($this->getFrom());
221
        $end = new \DateTime($this->getTo());
222
        $diff = $start->diff($end);
223
        return $diff->format('%a');
224
    }
225
226
    /**
227
     * Método que entrega los días que faltan para que la firma expire
228
     * @return int Días que faltan para que la firma expire
229
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
230
     * @version 2019-02-12
231
     */
232
    public function getExpirationDays($desde = null)
233
    {
234
        if (!$desde) {
235
            $desde = date('Y-m-d H:i:s');
236
        }
237
        $start = new \DateTime($desde);
238
        $end = new \DateTime($this->getTo());
239
        $diff = $start->diff($end);
240
        return $diff->format('%a');
241
    }
242
243
    /**
244
     * Método que entrega el nombre del emisor de la firma
245
     * @return CN del issuer
246
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
247
     * @version 2015-09-22
248
     */
249
    public function getIssuer()
250
    {
251
        return $this->data['issuer']['CN'];
252
    }
253
254
    /**
255
     * Método que entrega los datos del certificado
256
     * @return Arreglo con todo los datos del certificado
257
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
258
     * @version 2015-09-11
259
     */
260
    public function getData()
261
    {
262
        return $this->data;
263
    }
264
265
    /**
266
     * Método que obtiene el módulo de la clave privada
267
     * @return Módulo en base64
268
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
269
     * @version 2014-12-07
270
     */
271
    public function getModulus()
272
    {
273
        $details = openssl_pkey_get_details(openssl_pkey_get_private($this->certs['pkey']));
274
        return wordwrap(base64_encode($details['rsa']['n']), $this->config['wordwrap'], "\n", true);
275
    }
276
277
    /**
278
     * Método que obtiene el exponente público de la clave privada
279
     * @return Exponente público en base64
280
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
281
     * @version 2014-12-06
282
     */
283
    public function getExponent()
284
    {
285
        $details = openssl_pkey_get_details(openssl_pkey_get_private($this->certs['pkey']));
286
        return wordwrap(base64_encode($details['rsa']['e']), $this->config['wordwrap'], "\n", true);
287
    }
288
289
    /**
290
     * Método que entrega el certificado de la firma
291
     * @return Contenido del certificado, clave pública del certificado digital, en base64
292
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
293
     * @version 2015-08-24
294
     */
295 View Code Duplication
    public function getCertificate($clean = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
    {
297
        if ($clean) {
298
            return trim(str_replace(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return trim(str_replace(...$this->certs['cert'])); (string) is incompatible with the return type documented by sasco\LibreDTE\FirmaElectronica::getCertificate of type sasco\LibreDTE\Contenido.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
299
                ['-----BEGIN CERTIFICATE-----', '-----END CERTIFICATE-----'],
300
                '',
301
                $this->certs['cert']
302
            ));
303
        } else {
304
            return $this->certs['cert'];
305
        }
306
    }
307
308
    /**
309
     * Método que entrega la clave privada de la firma
310
     * @return Contenido de la clave privada del certificado digital en base64
311
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
312
     * @version 2015-08-24
313
     */
314 View Code Duplication
    public function getPrivateKey($clean = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
    {
316
        if ($clean) {
317
            return trim(str_replace(
0 ignored issues
show
Bug Best Practice introduced by
The return type of return trim(str_replace(...$this->certs['pkey'])); (string) is incompatible with the return type documented by sasco\LibreDTE\FirmaElectronica::getPrivateKey of type sasco\LibreDTE\Contenido.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
318
                ['-----BEGIN PRIVATE KEY-----', '-----END PRIVATE KEY-----'],
319
                '',
320
                $this->certs['pkey']
321
            ));
322
        } else {
323
            return $this->certs['pkey'];
324
        }
325
    }
326
327
    /**
328
     * Método para realizar la firma de datos
329
     * @param data Datos que se desean firmar
330
     * @param signature_alg Algoritmo que se utilizará para firmar (por defect SHA1)
331
     * @return Firma digital de los datos en base64 o =false si no se pudo firmar
332
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
333
     * @version 2014-12-08
334
     */
335
    public function sign($data, $signature_alg = OPENSSL_ALGO_SHA1)
336
    {
337
        $signature = null;
338
        if (openssl_sign($data, $signature, $this->certs['pkey'], $signature_alg)==false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
339
            return $this->error('No fue posible firmar los datos');
0 ignored issues
show
Documentation introduced by
'No fue posible firmar los datos' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
340
        }
341
        return base64_encode($signature);
342
    }
343
344
    /**
345
     * Método que verifica la firma digital de datos
346
     * @param data Datos que se desean verificar
347
     * @param signature Firma digital de los datos en base64
348
     * @param pub_key Certificado digital, clave pública, de la firma
349
     * @param signature_alg Algoritmo que se usó para firmar (por defect SHA1)
350
     * @return =true si la firma está ok, =false si está mal o no se pudo determinar
0 ignored issues
show
Documentation introduced by
The doc-type =true could not be parsed: Unknown type name "=true" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
351
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
352
     * @version 2014-12-08
353
     */
354
    public function verify($data, $signature, $pub_key = null, $signature_alg = OPENSSL_ALGO_SHA1)
355
    {
356
        if ($pub_key === null)
357
            $pub_key = $this->certs['cert'];
358
        $pub_key = $this->normalizeCert($pub_key);
359
        return openssl_verify($data, base64_decode($signature), $pub_key, $signature_alg) == 1 ? true : false;
360
    }
361
362
    /**
363
     * Método que firma un XML utilizando RSA y SHA1
364
     *
365
     * Referencia: http://www.di-mgt.com.au/xmldsig2.html
366
     *
367
     * @param xml Datos XML que se desean firmar
368
     * @param reference Referencia a la que hace la firma
369
     * @return XML firmado o =false si no se pudo fimar
370
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
371
     * @version 2017-10-22
372
     */
373
    public function signXML($xml, $reference = '', $tag = null, $xmlns_xsi = false)
374
    {
375
        // normalizar 4to parámetro que puede ser boolean o array
376
        if (is_array($xmlns_xsi)) {
377
            $namespace = $xmlns_xsi;
378
            $xmlns_xsi = false;
379
        } else {
380
            $namespace = null;
381
        }
382
        // obtener objeto del XML que se va a firmar
383
        $doc = new XML();
384
        $doc->loadXML($xml);
385
        if (!$doc->documentElement) {
386
            return $this->error('No se pudo obtener el documentElement desde el XML a firmar (posible XML mal formado)');
0 ignored issues
show
Documentation introduced by
'No se pudo obtener el d...sible XML mal formado)' is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
387
        }
388
        // crear nodo para la firma
389
        $Signature = $doc->importNode((new XML())->generate([
390
            'Signature' => [
391
                '@attributes' => $namespace ? false : [
392
                    'xmlns' => 'http://www.w3.org/2000/09/xmldsig#',
393
                ],
394
                'SignedInfo' => [
395
                    '@attributes' => $namespace ? false : [
396
                        'xmlns' => 'http://www.w3.org/2000/09/xmldsig#',
397
                        'xmlns:xsi' => $xmlns_xsi ? 'http://www.w3.org/2001/XMLSchema-instance' : false,
398
                    ],
399
                    'CanonicalizationMethod' => [
400
                        '@attributes' => [
401
                            'Algorithm' => 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
402
                        ],
403
                    ],
404
                    'SignatureMethod' => [
405
                        '@attributes' => [
406
                            'Algorithm' => 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
407
                        ],
408
                    ],
409
                    'Reference' => [
410
                        '@attributes' => [
411
                            'URI' => $reference,
412
                        ],
413
                        'Transforms' => [
414
                            'Transform' => [
415
                                '@attributes' => [
416
                                    'Algorithm' => $namespace ? 'http://www.altova.com' : 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
417
                                ],
418
                            ],
419
                        ],
420
                        'DigestMethod' => [
421
                            '@attributes' => [
422
                                'Algorithm' => 'http://www.w3.org/2000/09/xmldsig#sha1',
423
                            ],
424
                        ],
425
                        'DigestValue' => null,
426
                    ],
427
                ],
428
                'SignatureValue' => null,
429
                'KeyInfo' => [
430
                    'KeyValue' => [
431
                        'RSAKeyValue' => [
432
                            'Modulus' => null,
433
                            'Exponent' => null,
434
                        ],
435
                    ],
436
                    'X509Data' => [
437
                        'X509Certificate' => null,
438
                    ],
439
                ],
440
            ],
441
        ], $namespace)->documentElement, true);
442
        // calcular DigestValue
443
        if ($tag) {
444
            $item = $doc->documentElement->getElementsByTagName($tag)->item(0);
445
            if (!$item) {
446
                return $this->error('No fue posible obtener el nodo con el tag '.$tag);
0 ignored issues
show
Documentation introduced by
'No fue posible obtener ...odo con el tag ' . $tag is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
447
            }
448
            $digest = base64_encode(sha1($item->C14N(), true));
449
        } else {
450
            $digest = base64_encode(sha1($doc->C14N(), true));
451
        }
452
        $Signature->getElementsByTagName('DigestValue')->item(0)->nodeValue = $digest;
453
        // calcular SignatureValue
454
        $SignedInfo = $doc->saveHTML($Signature->getElementsByTagName('SignedInfo')->item(0));
455
        $firma = $this->sign($SignedInfo);
0 ignored issues
show
Documentation introduced by
$SignedInfo is of type string, but the function expects a object<sasco\LibreDTE\Datos>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
456
        if (!$firma)
457
            return false;
458
        $signature = wordwrap($firma, $this->config['wordwrap'], "\n", true);
459
        // reemplazar valores en la firma de
460
        $Signature->getElementsByTagName('SignatureValue')->item(0)->nodeValue = $signature;
461
        $Signature->getElementsByTagName('Modulus')->item(0)->nodeValue = $this->getModulus();
462
        $Signature->getElementsByTagName('Exponent')->item(0)->nodeValue = $this->getExponent();
463
        $Signature->getElementsByTagName('X509Certificate')->item(0)->nodeValue = $this->getCertificate(true);
464
        // agregar y entregar firma
465
        $doc->documentElement->appendChild($Signature);
466
        return $doc->saveXML();
467
    }
468
469
    /**
470
     * Método que verifica la validez de la firma de un XML utilizando RSA y SHA1
471
     * @param xml_data Archivo XML que se desea validar
472
     * @return =true si la firma del documento XML es válida o =false si no lo es
0 ignored issues
show
Documentation introduced by
The doc-type =true could not be parsed: Unknown type name "=true" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
473
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
474
     * @version 2015-09-02
475
     */
476
    public function verifyXML($xml_data, $tag = null)
477
    {
478
        $doc = new XML();
479
        $doc->loadXML($xml_data);
480
        // preparar datos que se verificarán
481
        $SignaturesElements = $doc->documentElement->getElementsByTagName('Signature');
482
        $Signature = $doc->documentElement->removeChild($SignaturesElements->item($SignaturesElements->length-1));
483
        $SignedInfo = $Signature->getElementsByTagName('SignedInfo')->item(0);
484
        $SignedInfo->setAttribute('xmlns', $Signature->getAttribute('xmlns'));
485
        $signed_info = $doc->saveHTML($SignedInfo);
486
        $signature = $Signature->getElementsByTagName('SignatureValue')->item(0)->nodeValue;
487
        $pub_key = $Signature->getElementsByTagName('X509Certificate')->item(0)->nodeValue;
488
        // verificar firma
489
        if (!$this->verify($signed_info, $signature, $pub_key))
0 ignored issues
show
Documentation introduced by
$signed_info is of type string, but the function expects a object<sasco\LibreDTE\Datos>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
490
            return false;
491
        // verificar digest
492
        $digest_original = $Signature->getElementsByTagName('DigestValue')->item(0)->nodeValue;
493
        if ($tag) {
494
            $digest_calculado = base64_encode(sha1($doc->documentElement->getElementsByTagName($tag)->item(0)->C14N(), true));
495
        } else {
496
            $digest_calculado = base64_encode(sha1($doc->C14N(), true));
497
        }
498
        return $digest_original == $digest_calculado;
499
    }
500
501
    /**
502
     * Método que obtiene la clave asociada al módulo y exponente entregados
503
     * @param modulus Módulo de la clave
504
     * @param exponent Exponente de la clave
505
     * @return Entrega la clave asociada al módulo y exponente
506
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
507
     * @version 2015-09-19
508
     */
509
    public static function getFromModulusExponent($modulus, $exponent)
510
    {
511
        $rsa = new \phpseclib\Crypt\RSA();
512
        $modulus = new \phpseclib\Math\BigInteger(base64_decode($modulus), 256);
513
        $exponent = new \phpseclib\Math\BigInteger(base64_decode($exponent), 256);
514
        $rsa->loadKey(['n' => $modulus, 'e' => $exponent]);
515
        $rsa->setPublicKey();
516
        return $rsa->getPublicKey();
517
    }
518
519
}
520