Issues (209)

src/LightSaml/Credential/X509Certificate.php (5 issues)

1
<?php
2
3
/*
4
 * This file is part of the LightSAML-Core package.
5
 *
6
 * (c) Milos Tomic <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace LightSaml\Credential;
13
14
use LightSaml\Error\LightSamlException;
15
use LightSaml\Error\LightSamlSecurityException;
16
use LightSaml\SamlConstants;
17
use RobRichards\XMLSecLibs\XMLSecurityKey;
18
19
class X509Certificate
20
{
21
    private static $typeMap = [
22
        'RSA-SHA1' => XMLSecurityKey::RSA_SHA1,
23
        'RSA-SHA256' => XMLSecurityKey::RSA_SHA256,
24
        'RSA-SHA384' => XMLSecurityKey::RSA_SHA384,
25
        'RSA-SHA512' => XMLSecurityKey::RSA_SHA512,
26
    ];
27
28
    /** @var string */
29
    protected $data;
30
31
    /** @var array|null */
32
    protected $info;
33
34
    /** @var string */
35
    private $signatureAlgorithm;
36
37
    /**
38
     * @param string $filename
39
     *
40
     * @return X509Certificate
41
     */
42
    public static function fromFile($filename)
43
    {
44
        $result = new self();
45
        $result->loadFromFile($filename);
46
47
        return $result;
48
    }
49
50
    /**
51
     * @param string $data
52
     *
53
     * @return X509Certificate
54
     */
55
    public function setData($data)
56
    {
57
        $this->data = preg_replace('/\s+/', '', $data);
58
        $this->parse();
59
60
        return $this;
61
    }
62
63
    /**
64
     * @return string
65
     */
66
    public function getData()
67
    {
68
        return $this->data;
69
    }
70
71
    /**
72
     * @param string $data
73
     *
74
     * @return X509Certificate
75
     *
76
     * @throws \InvalidArgumentException
77
     */
78
    public function loadPem($data)
79
    {
80
        $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
81
        if (false == preg_match($pattern, $data, $matches)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing preg_match($pattern, $data, $matches) of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
82
            throw new \InvalidArgumentException('Invalid PEM encoded certificate');
83
        }
84
        $this->data = preg_replace('/\s+/', '', $matches[1]);
85
        $this->parse();
86
87
        return $this;
88
    }
89
90
    /**
91
     * @param string $filename
92
     *
93
     * @return X509Certificate
94
     *
95
     * @throws \InvalidArgumentException
96
     */
97
    public function loadFromFile($filename)
98
    {
99
        if (!is_file($filename)) {
100
            throw new \InvalidArgumentException(sprintf("File not found '%s'", $filename));
101
        }
102
        $content = file_get_contents($filename);
103
        $this->loadPem($content);
104
105
        return $this;
106
    }
107
108
    /**
109
     * @return string
110
     */
111
    public function toPem()
112
    {
113
        $result = "-----BEGIN CERTIFICATE-----\n".chunk_split($this->getData(), 64, "\n")."-----END CERTIFICATE-----\n";
114
115
        return $result;
116
    }
117
118
    public function parse()
119
    {
120
        if (false == $this->data) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->data of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
121
            throw new LightSamlException('Certificate data not set');
122
        }
123
124
        $res = openssl_x509_read($this->toPem());
125
        $this->info = openssl_x509_parse($res);
126
        $this->signatureAlgorithm = null;
127
        $signatureType = isset($this->info['signatureTypeSN']) ? $this->info['signatureTypeSN'] : '';
128
        if ($signatureType && isset(self::$typeMap[$signatureType])) {
129
            $this->signatureAlgorithm = self::$typeMap[$signatureType];
130
        } else {
131
            openssl_x509_export($res, $out, false);
132
            if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m', $out, $match)) {
133
                switch ($match[1]) {
134
                    case 'sha1WithRSAEncryption':
135
                    case 'sha1WithRSA':
136
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA1;
137
                        break;
138
                    case 'sha256WithRSAEncryption':
139
                    case 'sha256WithRSA':
140
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA256;
141
                        break;
142
                    case 'sha384WithRSAEncryption':
143
                    case 'sha384WithRSA':
144
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA384;
145
                        break;
146
                    case 'sha512WithRSAEncryption':
147
                    case 'sha512WithRSA':
148
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA512;
149
                        break;
150
                    case 'md5WithRSAEncryption':
151
                    case 'md5WithRSA':
152
                        $this->signatureAlgorithm = SamlConstants::XMLDSIG_DIGEST_MD5;
153
                        break;
154
                    default:
155
                }
156
            }
157
        }
158
159
        if (!$this->signatureAlgorithm) {
160
            throw new LightSamlSecurityException('Unrecognized signature algorithm');
161
        }
162
    }
163
164
    /**
165
     * @return string
166
     *
167
     * @throws \LightSaml\Error\LightSamlException
168
     */
169
    public function getName()
170
    {
171
        if (false == $this->info) {
172
            throw new LightSamlException('Certificate data not set');
173
        }
174
175
        return $this->info['name'];
176
    }
177
178
    /**
179
     * @return string
180
     *
181
     * @throws \LightSaml\Error\LightSamlException
182
     */
183
    public function getSubject()
184
    {
185
        if (false == $this->info) {
186
            throw new LightSamlException('Certificate data not set');
187
        }
188
189
        return $this->info['subject'];
190
    }
191
192
    /**
193
     * @return array
194
     *
195
     * @throws \LightSaml\Error\LightSamlException
196
     */
197
    public function getIssuer()
198
    {
199
        if (false == $this->info) {
200
            throw new LightSamlException('Certificate data not set');
201
        }
202
203
        return $this->info['issuer'];
204
    }
205
206
    /**
207
     * @return int
208
     *
209
     * @throws \LightSaml\Error\LightSamlException
210
     */
211
    public function getValidFromTimestamp()
212
    {
213
        if (false == $this->info) {
214
            throw new LightSamlException('Certificate data not set');
215
        }
216
217
        return $this->info['validFrom_time_t'];
218
    }
219
220
    /**
221
     * @return int
222
     *
223
     * @throws \LightSaml\Error\LightSamlException
224
     */
225
    public function getValidToTimestamp()
226
    {
227
        if (false == $this->info) {
228
            throw new LightSamlException('Certificate data not set');
229
        }
230
231
        return $this->info['validTo_time_t'];
232
    }
233
234
    /**
235
     * @return array
236
     *
237
     * @throws \LightSaml\Error\LightSamlException
238
     */
239
    public function getInfo()
240
    {
241
        if (false == $this->info) {
242
            throw new LightSamlException('Certificate data not set');
243
        }
244
245
        return $this->info;
246
    }
247
248
    /**
249
     * @throws \LightSaml\Error\LightSamlException
250
     *
251
     * @return string
252
     */
253
    public function getFingerprint()
254
    {
255
        if (false == $this->data) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->data of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
256
            throw new LightSamlException('Certificate data not set');
257
        }
258
259
        return XMLSecurityKey::getRawThumbprint($this->toPem());
0 ignored issues
show
Are you sure the usage of RobRichards\XMLSecLibs\X...mbprint($this->toPem()) targeting RobRichards\XMLSecLibs\X...Key::getRawThumbprint() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
260
    }
261
262
    public function getSignatureAlgorithm()
263
    {
264
        if (false == $this->data) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $this->data of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
265
            throw new LightSamlException('Certificate data not set');
266
        }
267
268
        return $this->signatureAlgorithm;
269
    }
270
}
271