Completed
Push — v2.0.x ( 7a58b6 )
by Florent
24:58
created

JWKFactory   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 32
c 1
b 0
f 0
lcom 0
cbo 4
dl 0
loc 244
rs 9.6

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 1 1
A createFromValues() 0 8 3
A createFromCertificateFile() 0 7 1
A createFromCertificate() 0 7 1
A createFromX509Resource() 0 7 1
A createFromFile() 0 7 1
A createFromDER() 0 7 1
A createFromPEM() 0 7 1
A createFromJKU() 0 10 3
B createFromX5U() 0 22 5
C createFromX5C() 0 41 8
B downloadContent() 0 30 6
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2015 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Factory;
13
14
use Jose\KeyConverter\KeyConverter;
15
use Jose\Object\JWK;
16
use Jose\Object\JWKSet;
17
use Jose\Util\StringUtil;
18
19
final class JWKFactory
20
{
21
    /**
22
     * KeyFactory constructor.
23
     *
24
     * This factory is not supposed to be instantiated
25
     */
26
    private function __construct() {}
27
28
    /**
29
     * @param array $values
30
     *
31
     * @return \Jose\Object\JWKInterface|\Jose\Object\JWKSetInterface
32
     */
33
    public static function createFromValues(array $values)
34
    {
35
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
36
            return new JWKSet($values);
37
        }
38
39
        return new JWK($values);
40
    }
41
42
    /**
43
     * @param string $file
44
     * @param array  $additional_values
45
     *
46
     * @return \Jose\Object\JWKInterface
47
     */
48
    public static function createFromCertificateFile($file, array $additional_values = [])
49
    {
50
        $values = KeyConverter::loadKeyFromCertificateFile($file);
51
        $values = array_merge($values, $additional_values);
52
53
        return new JWK($values);
54
    }
55
56
    /**
57
     * @param string $certificate
58
     * @param array  $additional_values
59
     *
60
     * @return \Jose\Object\JWKInterface
61
     */
62
    public static function createFromCertificate($certificate, array $additional_values = [])
63
    {
64
        $values = KeyConverter::loadKeyFromCertificate($certificate);
65
        $values = array_merge($values, $additional_values);
66
67
        return new JWK($values);
68
    }
69
70
    /**
71
     * @param resource $res
72
     * @param array    $additional_values
73
     *
74
     * @return \Jose\Object\JWKInterface
75
     */
76
    public static function createFromX509Resource($res, array $additional_values = [])
77
    {
78
        $values = KeyConverter::loadKeyFromX509Resource($res);
79
        $values = array_merge($values, $additional_values);
80
81
        return new JWK($values);
82
    }
83
84
    /**
85
     * @param string      $file
86
     * @param null|string $password
87
     * @param bool        $is_DER
88
     * @param array       $additional_values
89
     *
90
     * @return \Jose\Object\JWKInterface
91
     */
92
    public static function createFromFile($file, $password = null, $is_DER = false, array $additional_values = [])
93
    {
94
        $values = KeyConverter::loadKeyFromFile($file, $password, $is_DER);
95
        $values = array_merge($values, $additional_values);
96
97
        return new JWK($values);
98
    }
99
100
    /**
101
     * @param string      $der
102
     * @param null|string $password
103
     * @param array       $additional_values
104
     *
105
     * @return \Jose\Object\JWKInterface
106
     */
107
    public static function createFromDER($der, $password = null, array $additional_values = [])
108
    {
109
        $values = KeyConverter::loadKeyFromDER($der, $password);
110
        $values = array_merge($values, $additional_values);
111
112
        return new JWK($values);
113
    }
114
115
    /**
116
     * @param string      $pem
117
     * @param null|string $password
118
     * @param array       $additional_values
119
     *
120
     * @return \Jose\Object\JWKInterface
121
     */
122
    public static function createFromPEM($pem, $password = null, array $additional_values = [])
123
    {
124
        $values = KeyConverter::loadKeyFromPEM($pem, $password);
125
        $values = array_merge($values, $additional_values);
126
127
        return new JWK($values);
128
    }
129
130
    /**
131
     * @param string $jku
132
     * @param bool   $allow_unsecured_connection
133
     *
134
     * @return \Jose\Object\JWKSet
135
     */
136
    public static function createFromJKU($jku, $allow_unsecured_connection = false)
137
    {
138
        $content = self::downloadContent($jku, $allow_unsecured_connection);
139
        $content = json_decode($content, true);
140
        if (!is_array($content) || !array_key_exists('keys', $content)) {
141
            throw new \InvalidArgumentException('Invalid content.');
142
        }
143
144
        return new JWKSet($content);
145
    }
146
147
    /**
148
     * @param string $x5u
149
     * @param bool   $allow_unsecured_connection
150
     *
151
     * @return \Jose\Object\JWKSetInterface
152
     */
153
    public static function createFromX5U($x5u, $allow_unsecured_connection = false)
154
    {
155
        $content = self::downloadContent($x5u, $allow_unsecured_connection);
156
        $content = json_decode($content, true);
157
        if (!is_array($content)) {
158
            throw new \InvalidArgumentException('Invalid content.');
159
        }
160
161
        $jwkset = new JWKSet();
162
        foreach ($content as $kid => $cert) {
163
            $jwk = KeyConverter::loadKeyFromCertificate($cert);
164
            if (empty($jwk)) {
165
                throw new \InvalidArgumentException('Invalid content.');
166
            }
167
            if (is_string($kid)) {
168
                $jwk['kid'] = $kid;
169
            }
170
            $jwkset->addKey(new JWK($jwk));
171
        }
172
173
        return $jwkset;
174
    }
175
176
    /**
177
     * @param array $x5c
178
     * @param array $additional_values
179
     *
180
     * @return \Jose\Object\JWKInterface
181
     */
182
    public static function createFromX5C(array $x5c, array $additional_values = [])
183
    {
184
        $certificate = null;
185
        $last_issuer = null;
186
        $last_subject = null;
187
        foreach ($x5c as $cert) {
188
            $current_cert = "-----BEGIN CERTIFICATE-----\n$cert\n-----END CERTIFICATE-----";
189
            $x509 = openssl_x509_read($current_cert);
190
            if (false === $x509) {
191
                $last_issuer = null;
192
                $last_subject = null;
193
                break;
194
            }
195
            $parsed = openssl_x509_parse($x509);
196
            openssl_x509_free($x509);
197
            if (false === $parsed) {
198
                $last_issuer = null;
199
                $last_subject = null;
200
                break;
201
            }
202
            if (null === $last_subject) {
203
                $last_subject = $parsed['subject'];
204
                $last_issuer = $parsed['issuer'];
205
                $certificate = $current_cert;
206
            } else {
207
                if (json_encode($last_issuer) === json_encode($parsed['subject'])) {
208
                    $last_subject = $parsed['subject'];
209
                    $last_issuer = $parsed['issuer'];
210
                } else {
211
                    $last_issuer = null;
212
                    $last_subject = null;
213
                    break;
214
                }
215
            }
216
        }
217
        if (null === $last_issuer || json_encode($last_issuer) !== json_encode($last_subject)) {
218
            throw new \InvalidArgumentException('Invalid certificate chain.');
219
        }
220
221
        return self::createFromCertificate($certificate, $additional_values);
222
    }
223
224
    /**
225
     * @param string $url
226
     * @param bool   $allow_unsecured_connection
227
     *
228
     * @throws \InvalidArgumentException
229
     *
230
     * @return string
231
     */
232
    private static function downloadContent($url, $allow_unsecured_connection)
233
    {
234
        // The URL must be a valid URL and scheme must be https
235
        if (false === filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
236
            throw new \InvalidArgumentException('Invalid URL.');
237
        }
238
        if (false === $allow_unsecured_connection && 'https://' !==  StringUtil::substr($url, 0, 8)) {
239
            throw new \InvalidArgumentException('Unsecured connection.');
240
        }
241
242
        $params = [
243
            CURLOPT_RETURNTRANSFER => true,
244
            CURLOPT_URL            => $url,
245
        ];
246
        if (true === $allow_unsecured_connection) {
247
            $params[CURLOPT_SSL_VERIFYPEER] = true;
248
            $params[CURLOPT_SSL_VERIFYHOST] = 2;
249
        }
250
251
        $ch = curl_init();
252
        curl_setopt_array($ch, $params);
253
        $content = curl_exec($ch);
254
        curl_close($ch);
255
256
        if (empty($content)) {
257
            throw new \InvalidArgumentException('Unable to get content.');
258
        }
259
260
        return $content;
261
    }
262
}
263