Failed Conditions
Push — UrlJWKSet ( 7ceb40...22eb3b )
by Florent
02:53
created

JWKFactory   B

Complexity

Total Complexity 31

Size/Duplication

Total Lines 328
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 18
dl 0
loc 328
rs 7.1866
c 0
b 0
f 0

24 Methods

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