Completed
Push — v2.0.x ( 94c950...c40792 )
by Florent
02:29
created

JWKFactory::createFromKeyFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 3
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 array       $additional_values
80
     *
81
     * @return \Jose\Object\JWKInterface
82
     */
83
    public static function createFromKeyFile($file, $password = null, array $additional_values = [])
84
    {
85
        $values = KeyConverter::loadFromKeyFile($file, $password);
86
        $values = array_merge($values, $additional_values);
87
88
        return new JWK($values);
89
    }
90
91
    /**
92
     * @param string      $key
93
     * @param null|string $password
94
     * @param array       $additional_values
95
     *
96
     * @return \Jose\Object\JWKInterface
97
     */
98
    public static function createFromKey($key, $password = null, array $additional_values = [])
99
    {
100
        $values = KeyConverter::loadFromKey($key, $password);
101
        $values = array_merge($values, $additional_values);
102
103
        return new JWK($values);
104
    }
105
106
    /**
107
     * @param string $jku
108
     * @param bool   $allow_unsecured_connection
109
     *
110
     * @return \Jose\Object\JWKSet
111
     */
112
    public static function createFromJKU($jku, $allow_unsecured_connection = false)
113
    {
114
        $content = self::downloadContent($jku, $allow_unsecured_connection);
115
        $content = json_decode($content, true);
116
        if (!is_array($content) || !array_key_exists('keys', $content)) {
117
            throw new \InvalidArgumentException('Invalid content.');
118
        }
119
120
        return new JWKSet($content);
121
    }
122
123
    /**
124
     * @param string $x5u
125
     * @param bool   $allow_unsecured_connection
126
     *
127
     * @return \Jose\Object\JWKSetInterface
128
     */
129
    public static function createFromX5U($x5u, $allow_unsecured_connection = false)
130
    {
131
        $content = self::downloadContent($x5u, $allow_unsecured_connection);
132
        $content = json_decode($content, true);
133
        if (!is_array($content)) {
134
            throw new \InvalidArgumentException('Invalid content.');
135
        }
136
137
        $jwkset = new JWKSet();
138
        foreach ($content as $kid => $cert) {
139
            $jwk = KeyConverter::loadKeyFromCertificate($cert);
140
            if (empty($jwk)) {
141
                throw new \InvalidArgumentException('Invalid content.');
142
            }
143
            if (is_string($kid)) {
144
                $jwk['kid'] = $kid;
145
            }
146
            $jwkset = $jwkset->addKey(new JWK($jwk));
147
        }
148
149
        return $jwkset;
150
    }
151
152
    /**
153
     * @param array $x5c
154
     * @param array $additional_values
155
     *
156
     * @return \Jose\Object\JWKInterface
157
     */
158
    public static function createFromX5C(array $x5c, array $additional_values = [])
159
    {
160
        $certificate = null;
161
        $last_issuer = null;
162
        $last_subject = null;
163
        foreach ($x5c as $cert) {
164
            $current_cert = "-----BEGIN CERTIFICATE-----\n$cert\n-----END CERTIFICATE-----";
165
            $x509 = openssl_x509_read($current_cert);
166
            if (false === $x509) {
167
                $last_issuer = null;
168
                $last_subject = null;
169
                break;
170
            }
171
            $parsed = openssl_x509_parse($x509);
172
            openssl_x509_free($x509);
173
            if (false === $parsed) {
174
                $last_issuer = null;
175
                $last_subject = null;
176
                break;
177
            }
178
            if (null === $last_subject) {
179
                $last_subject = $parsed['subject'];
180
                $last_issuer = $parsed['issuer'];
181
                $certificate = $current_cert;
182
            } else {
183
                if (json_encode($last_issuer) === json_encode($parsed['subject'])) {
184
                    $last_subject = $parsed['subject'];
185
                    $last_issuer = $parsed['issuer'];
186
                } else {
187
                    $last_issuer = null;
188
                    $last_subject = null;
189
                    break;
190
                }
191
            }
192
        }
193
        if (null === $last_issuer || json_encode($last_issuer) !== json_encode($last_subject)) {
194
            throw new \InvalidArgumentException('Invalid certificate chain.');
195
        }
196
197
        return self::createFromCertificate($certificate, $additional_values);
198
    }
199
200
    /**
201
     * @param string $url
202
     * @param bool   $allow_unsecured_connection
203
     *
204
     * @throws \InvalidArgumentException
205
     *
206
     * @return string
207
     */
208
    private static function downloadContent($url, $allow_unsecured_connection)
209
    {
210
        // The URL must be a valid URL and scheme must be https
211
        if (false === filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED)) {
212
            throw new \InvalidArgumentException('Invalid URL.');
213
        }
214
        if (false === $allow_unsecured_connection && 'https://' !==  substr($url, 0, 8)) {
215
            throw new \InvalidArgumentException('Unsecured connection.');
216
        }
217
218
        $params = [
219
            CURLOPT_RETURNTRANSFER => true,
220
            CURLOPT_URL            => $url,
221
        ];
222
        if (true === $allow_unsecured_connection) {
223
            $params[CURLOPT_SSL_VERIFYPEER] = true;
224
            $params[CURLOPT_SSL_VERIFYHOST] = 2;
225
        }
226
227
        $ch = curl_init();
228
        curl_setopt_array($ch, $params);
229
        $content = curl_exec($ch);
230
        curl_close($ch);
231
232
        if (empty($content)) {
233
            throw new \InvalidArgumentException('Unable to get content.');
234
        }
235
236
        return $content;
237
    }
238
}
239