Completed
Push — v2.0.x ( 2b2b8e...21aa40 )
by Florent
03:32
created

JWKFactory::createFromX5C()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 41
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

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