Failed Conditions
Push — v7 ( 318c5f...2b71c7 )
by Florent
03:43
created

JWKFactory::createFromValues()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 1
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 Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Http\Client\HttpClient;
17
use Http\Message\RequestFactory;
18
use Jose\KeyConverter\ECKey;
19
use Jose\KeyConverter\KeyConverter;
20
use Jose\KeyConverter\RSAKey;
21
use Jose\Object\JKUJWKSet;
22
use Jose\Object\JWK;
23
use Jose\Object\JWKInterface;
24
use Jose\Object\JWKSet;
25
use Jose\Object\JWKSetInterface;
26
use Jose\Object\JWKSets;
27
use Jose\Object\PublicJWKSet;
28
use Jose\Object\RotatableJWKSet;
29
use Jose\Object\StorableJWK;
30
use Jose\Object\StorableJWKSet;
31
use Jose\Object\X5UJWKSet;
32
33
final class JWKFactory
34
{
35
    /**
36
     * @param JWKSetInterface $jwkset
37
     *
38
     * @return JWKSetInterface
39
     */
40
    public static function createPublicKeySet(JWKSetInterface $jwkset): JWKSetInterface
41
    {
42
        return new PublicJWKSet($jwkset);
43
    }
44
45
    /**
46
     * @param array $jwksets
47
     *
48
     * @return JWKSetInterface
49
     */
50
    public static function createKeySets(array $jwksets = []): JWKSetInterface
51
    {
52
        return new JWKSets($jwksets);
53
    }
54
55
    /**
56
     * @param string $filename
57
     * @param array  $parameters
58
     *
59
     * @return JWKInterface
60
     */
61
    public static function createStorableKey(string $filename, array $parameters): JWKInterface
62
    {
63
        return new StorableJWK($filename, $parameters);
64
    }
65
66
    /**
67
     * @param string $filename
68
     * @param array  $parameters
69
     * @param int    $nb_keys
70
     *
71
     * @return JWKSetInterface
72
     */
73
    public static function createRotatableKeySet(string $filename, array $parameters, int $nb_keys): JWKSetInterface
74
    {
75
        return new RotatableJWKSet($filename, $parameters, $nb_keys);
76
    }
77
78
    /**
79
     * @param string $filename
80
     * @param array  $parameters
81
     * @param int    $nb_keys
82
     *
83
     * @return JWKSetInterface
84
     */
85
    public static function createStorableKeySet(string $filename, array $parameters, int $nb_keys): JWKSetInterface
86
    {
87
        return new StorableJWKSet($filename, $parameters, $nb_keys);
88
    }
89
90
    /**
91
     * @param array $config
92
     *
93
     * @return JWKInterface
94
     */
95
    public static function createKey(array $config): JWKInterface
96
    {
97
        Assertion::keyExists($config, 'kty', 'The key "kty" must be set');
98
        $supported_types = ['RSA' => 'RSA', 'OKP' => 'OKP', 'EC' => 'EC', 'oct' => 'Oct', 'none' => 'None'];
99
        $kty = $config['kty'];
100
        Assertion::keyExists($supported_types, $kty, sprintf('The key type "%s" is not supported. Please use one of %s', $kty, json_encode(array_keys($supported_types))));
101
        $method = sprintf('create%sKey', $supported_types[$kty]);
102
103
        return self::$method($config);
104
    }
105
106
    /**
107
     * @param array $values
108
     *
109
     * @return JWKInterface
110
     */
111
    public static function createRSAKey(array $values): JWKInterface
112
    {
113
        Assertion::keyExists($values, 'size', 'The key size is not set.');
114
        $size = $values['size'];
115
        unset($values['size']);
116
117
        Assertion::true(0 === $size % 8, 'Invalid key size.');
118
        Assertion::greaterOrEqualThan($size, 384, 'Key length is too short. It needs to be at least 384 bits.');
119
120
        $key = openssl_pkey_new([
121
            'private_key_bits' => $size,
122
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
123
        ]);
124
        openssl_pkey_export($key, $out);
125
        $rsa = new RSAKey($out);
126
        $values = array_merge(
127
            $values,
128
            $rsa->toArray()
129
        );
130
131
        return new JWK($values);
132
    }
133
134
    /**
135
     * @param array $values
136
     *
137
     * @return JWKInterface
138
     */
139
    public static function createECKey(array $values): JWKInterface
140
    {
141
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
142
        $curve = $values['crv'];
143
        $args = [
144
            'curve_name'       => self::getOpensslName($curve),
145
            'private_key_type' => OPENSSL_KEYTYPE_EC,
146
        ];
147
        $key = openssl_pkey_new($args);
148
        $res = openssl_pkey_export($key, $out);
149
        Assertion::true($res, 'Unable to create the key');
150
151
        $rsa = new ECKey($out);
152
        $values = array_merge(
153
            $values,
154
            $rsa->toArray()
155
        );
156
157
        return new JWK($values);
158
    }
159
160
    /**
161
     * @param array $values
162
     *
163
     * @return JWKInterface
164
     */
165
    public static function createOctKey(array $values): JWKInterface
166
    {
167
        Assertion::keyExists($values, 'size', 'The key size is not set.');
168
        $size = $values['size'];
169
        unset($values['size']);
170
        Assertion::true(0 === $size % 8, 'Invalid key size.');
171
        $values = array_merge(
172
            $values,
173
            [
174
                'kty' => 'oct',
175
                'k'   => Base64Url::encode(random_bytes($size / 8)),
176
            ]
177
        );
178
179
        return new JWK($values);
180
    }
181
182
    /**
183
     * @param array $values
184
     *
185
     * @return JWKInterface
186
     */
187
    public static function createOKPKey(array $values): JWKInterface
188
    {
189
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
190
        $curve = $values['crv'];
191
        switch ($curve) {
192
            case 'X25519':
193
                Assertion::true(function_exists('curve25519_public'), sprintf('Unsupported "%s" curve', $curve));
194
                $d = random_bytes(32);
195
                $x = curve25519_public($d);
196
                break;
197
            case 'Ed25519':
198
                Assertion::true(function_exists('ed25519_publickey'), sprintf('Unsupported "%s" curve', $curve));
199
                $d = random_bytes(32);
200
                $x = ed25519_publickey($d);
201
                break;
202
            default:
203
                throw new \InvalidArgumentException(sprintf('Unsupported "%s" curve', $curve));
204
        }
205
206
        $values = array_merge(
207
            $values,
208
            [
209
                'kty' => 'OKP',
210
                'crv' => $curve,
211
                'x'   => Base64Url::encode($x),
212
                'd'   => Base64Url::encode($d),
213
            ]
214
        );
215
216
        return new JWK($values);
217
    }
218
219
    /**
220
     * @param array $values
221
     *
222
     * @return JWKInterface
223
     */
224
    public static function createNoneKey(array $values): JWKInterface
225
    {
226
        $values = array_merge(
227
            $values,
228
            [
229
                'kty' => 'none',
230
                'alg' => 'none',
231
                'use' => 'sig',
232
            ]
233
        );
234
235
        return new JWK($values);
236
    }
237
238
    /**
239
     * @param string $curve
240
     *
241
     * @throws \InvalidArgumentException
242
     *
243
     * @return string
244
     */
245
    private static function getOpensslName(string $curve): string
246
    {
247
        switch ($curve) {
248
            case 'P-256':
249
                return 'prime256v1';
250
            case 'P-384':
251
                return 'secp384r1';
252
            case 'P-521':
253
                return 'secp521r1';
254
            default:
255
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
256
        }
257
    }
258
259
    /**
260
     * @param array $values
261
     *
262
     * @return JWKInterface|JWKSetInterface
263
     */
264
    public static function createFromValues(array $values)
265
    {
266
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
267
            return new JWKSet($values);
268
        }
269
270
        return new JWK($values);
271
    }
272
273
    /**
274
     * @param string $file
275
     * @param array  $additional_values
276
     *
277
     * @return JWKInterface
278
     */
279
    public static function createFromCertificateFile(string $file, array $additional_values = []): JWKInterface
280
    {
281
        $values = KeyConverter::loadKeyFromCertificateFile($file);
282
        $values = array_merge($values, $additional_values);
283
284
        return new JWK($values);
285
    }
286
287
    /**
288
     * @param string $certificate
289
     * @param array $additional_values
290
     * @return JWKInterface
291
     */
292
    public static function createFromCertificate(string $certificate, array $additional_values = []): JWKInterface
293
    {
294
        $values = KeyConverter::loadKeyFromCertificate($certificate);
295
        $values = array_merge($values, $additional_values);
296
297
        return new JWK($values);
298
    }
299
300
    /**
301
     * @param       $res
302
     * @param array $additional_values
303
     *
304
     * @return JWKInterface
305
     */
306
    public static function createFromX509Resource($res, array $additional_values = []): JWKInterface
307
    {
308
        Assertion::true(is_resource($res));
309
        $values = KeyConverter::loadKeyFromX509Resource($res);
310
        $values = array_merge($values, $additional_values);
311
312
        return new JWK($values);
313
    }
314
315
    /**
316
     * @param string      $file
317
     * @param null|string $password
318
     * @param array       $additional_values
319
     *
320
     * @return JWKInterface
321
     */
322
    public static function createFromKeyFile(string $file, ?string $password = null, array $additional_values = []): JWKInterface
323
    {
324
        $values = KeyConverter::loadFromKeyFile($file, $password);
325
        $values = array_merge($values, $additional_values);
326
327
        return new JWK($values);
328
    }
329
330
    /**
331
     * @param string      $key
332
     * @param null|string $password
333
     * @param array       $additional_values
334
     *
335
     * @return JWKInterface
336
     */
337
    public static function createFromKey(string $key, ?string $password = null, array $additional_values = []): JWKInterface
338
    {
339
        $values = KeyConverter::loadFromKey($key, $password);
340
        $values = array_merge($values, $additional_values);
341
342
        return new JWK($values);
343
    }
344
345
    /**
346
     * @param RequestFactory $requestFactory
347
     * @param HttpClient     $client
348
     * @param string         $jku
349
     * @param bool           $allow_http_connection
350
     *
351
     * @return JWKSetInterface
352
     */
353
    public static function createFromJKU(RequestFactory $requestFactory, HttpClient $client, string $jku, bool $allow_http_connection = false): JWKSetInterface
354
    {
355
        return new JKUJWKSet($requestFactory, $client, $jku, $allow_http_connection);
356
    }
357
358
    /**
359
     * @param RequestFactory $requestFactory
360
     * @param HttpClient     $client
361
     * @param string         $x5u
362
     * @param bool           $allow_http_connection
363
     *
364
     * @return JWKSetInterface
365
     */
366
    public static function createFromX5U(RequestFactory $requestFactory, HttpClient $client, string $x5u, bool $allow_http_connection = false): JWKSetInterface
367
    {
368
        return new X5UJWKSet($requestFactory, $client, $x5u, $allow_http_connection);
369
    }
370
371
    /**
372
     * @param array $x5c
373
     * @param array $additional_values
374
     *
375
     * @return JWKInterface
376
     */
377
    public static function createFromX5C(array $x5c, array $additional_values = []): JWKInterface
378
    {
379
        $values = KeyConverter::loadFromX5C($x5c);
380
        $values = array_merge($values, $additional_values);
381
382
        return new JWK($values);
383
    }
384
385
    /**
386
     * @param JWKSetInterface $jwk_set
387
     * @param int             $key_index
388
     *
389
     * @return JWKInterface
390
     */
391
    public static function createFromKeySet(JWKSetInterface $jwk_set, int $key_index): JWKInterface
392
    {
393
        return $jwk_set->getKey($key_index);
394
    }
395
}
396