X509Certificate   A
last analyzed

Complexity

Total Complexity 41

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 89.36%

Importance

Changes 0
Metric Value
dl 0
loc 252
c 0
b 0
f 0
wmc 41
lcom 1
cbo 3
ccs 84
cts 94
cp 0.8936
rs 9.1199

15 Methods

Rating   Name   Duplication   Size   Complexity  
A fromFile() 0 7 1
A setData() 0 7 1
A getData() 0 4 1
A loadPem() 0 11 2
A loadFromFile() 0 10 2
A toPem() 0 6 1
C parse() 0 45 17
A getName() 0 8 2
A getSubject() 0 8 2
A getIssuer() 0 8 2
A getValidFromTimestamp() 0 8 2
A getValidToTimestamp() 0 8 2
A getInfo() 0 8 2
A getFingerprint() 0 8 2
A getSignatureAlgorithm() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like X509Certificate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use X509Certificate, and based on these observations, apply Extract Interface, too.

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 null|array */
32
    protected $info;
33
34
    /** @var string */
35
    private $signatureAlgorithm;
36
37
    /**
38
     * @param string $filename
39
     *
40
     * @return X509Certificate
41
     */
42 33
    public static function fromFile($filename)
43
    {
44 33
        $result = new self();
45 33
        $result->loadFromFile($filename);
46
47 33
        return $result;
48
    }
49
50
    /**
51
     * @param string $data
52
     *
53
     * @return X509Certificate
54
     */
55 41
    public function setData($data)
56
    {
57 41
        $this->data = preg_replace('/\s+/', '', $data);
58 41
        $this->parse();
59
60 41
        return $this;
61
    }
62
63
    /**
64
     * @return string
65
     */
66 85
    public function getData()
67
    {
68 85
        return $this->data;
69
    }
70
71
    /**
72
     * @param string $data
73
     *
74
     * @return X509Certificate
75
     *
76
     * @throws \InvalidArgumentException
77
     */
78 58
    public function loadPem($data)
79
    {
80 58
        $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m';
81 58
        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 1
            throw new \InvalidArgumentException('Invalid PEM encoded certificate');
83
        }
84 57
        $this->data = preg_replace('/\s+/', '', $matches[1]);
85 57
        $this->parse();
86
87 57
        return $this;
88
    }
89
90
    /**
91
     * @param string $filename
92
     *
93
     * @return X509Certificate
94
     *
95
     * @throws \InvalidArgumentException
96
     */
97 58
    public function loadFromFile($filename)
98
    {
99 58
        if (!is_file($filename)) {
100 1
            throw new \InvalidArgumentException(sprintf("File not found '%s'", $filename));
101
        }
102 57
        $content = file_get_contents($filename);
103 57
        $this->loadPem($content);
104
105 57
        return $this;
106
    }
107
108
    /**
109
     * @return string
110
     */
111 85
    public function toPem()
112
    {
113 85
        $result = "-----BEGIN CERTIFICATE-----\n".chunk_split($this->getData(), 64, "\n")."-----END CERTIFICATE-----\n";
114
115 85
        return $result;
116
    }
117
118 86
    public function parse()
119
    {
120 86
        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 1
            throw new LightSamlException('Certificate data not set');
122
        }
123
124 85
        $res = openssl_x509_read($this->toPem());
125 85
        $this->info = openssl_x509_parse($res);
126 85
        $this->signatureAlgorithm = null;
127 85
        $signatureType = isset($this->info['signatureTypeSN']) ? $this->info['signatureTypeSN'] : '';
128 85
        if ($signatureType && isset(self::$typeMap[$signatureType])) {
129 78
            $this->signatureAlgorithm = self::$typeMap[$signatureType];
130
        } else {
131 17
            openssl_x509_export($res, $out, false);
132 17
            if (preg_match('/^\s+Signature Algorithm:\s*(.*)\s*$/m', $out, $match)) {
133 17
                switch ($match[1]) {
134 17
                    case 'sha1WithRSAEncryption':
135 17
                    case 'sha1WithRSA':
136 16
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA1;
137 16
                        break;
138 2
                    case 'sha256WithRSAEncryption':
139 2
                    case 'sha256WithRSA':
140
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA256;
141
                        break;
142 2
                    case 'sha384WithRSAEncryption':
143 2
                    case 'sha384WithRSA':
144
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA384;
145
                        break;
146 2
                    case 'sha512WithRSAEncryption':
147 2
                    case 'sha512WithRSA':
148
                        $this->signatureAlgorithm = XMLSecurityKey::RSA_SHA512;
149
                        break;
150 2
                    case 'md5WithRSAEncryption':
151
                    case 'md5WithRSA':
152 2
                        $this->signatureAlgorithm = SamlConstants::XMLDSIG_DIGEST_MD5;
153 2
                        break;
154
                    default:
155
                }
156
            }
157
        }
158
159 85
        if (!$this->signatureAlgorithm) {
160
            throw new LightSamlSecurityException('Unrecognized signature algorithm');
161
        }
162 85
    }
163
164
    /**
165
     * @return string
166
     *
167
     * @throws \LightSaml\Error\LightSamlException
168
     */
169 24
    public function getName()
170
    {
171 24
        if (false == $this->info) {
172 1
            throw new LightSamlException('Certificate data not set');
173
        }
174
175 23
        return $this->info['name'];
176
    }
177
178
    /**
179
     * @return string
180
     *
181
     * @throws \LightSaml\Error\LightSamlException
182
     */
183 3
    public function getSubject()
184
    {
185 3
        if (false == $this->info) {
186 1
            throw new LightSamlException('Certificate data not set');
187
        }
188
189 2
        return $this->info['subject'];
190
    }
191
192
    /**
193
     * @return array
194
     *
195
     * @throws \LightSaml\Error\LightSamlException
196
     */
197 2
    public function getIssuer()
198
    {
199 2
        if (false == $this->info) {
200 1
            throw new LightSamlException('Certificate data not set');
201
        }
202
203 1
        return $this->info['issuer'];
204
    }
205
206
    /**
207
     * @return int
208
     *
209
     * @throws \LightSaml\Error\LightSamlException
210
     */
211 2
    public function getValidFromTimestamp()
212
    {
213 2
        if (false == $this->info) {
214 1
            throw new LightSamlException('Certificate data not set');
215
        }
216
217 1
        return $this->info['validFrom_time_t'];
218
    }
219
220
    /**
221
     * @return int
222
     *
223
     * @throws \LightSaml\Error\LightSamlException
224
     */
225 2
    public function getValidToTimestamp()
226
    {
227 2
        if (false == $this->info) {
228 1
            throw new LightSamlException('Certificate data not set');
229
        }
230
231 1
        return $this->info['validTo_time_t'];
232
    }
233
234
    /**
235
     * @return array
236
     *
237
     * @throws \LightSaml\Error\LightSamlException
238
     */
239 3
    public function getInfo()
240
    {
241 3
        if (false == $this->info) {
242 1
            throw new LightSamlException('Certificate data not set');
243
        }
244
245 2
        return $this->info;
246
    }
247
248
    /**
249
     * @throws \LightSaml\Error\LightSamlException
250
     *
251
     * @return string
252
     */
253 3
    public function getFingerprint()
254
    {
255 3
        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 1
            throw new LightSamlException('Certificate data not set');
257
        }
258
259 2
        return XMLSecurityKey::getRawThumbprint($this->toPem());
260
    }
261
262 35
    public function getSignatureAlgorithm()
263
    {
264 35
        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 35
        return $this->signatureAlgorithm;
269
    }
270
}
271