Failed Conditions
Push — ECKeyAndGCm ( a1377c )
by Florent
04:10
created

JWKFactory::createRotatableKeySet()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
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 Assert\Assertion;
15
use Base64Url\Base64Url;
16
use Jose\KeyConverter\ECKey;
17
use Jose\KeyConverter\KeyConverter;
18
use Jose\KeyConverter\RSAKey;
19
use Jose\Object\JKUJWKSet;
20
use Jose\Object\JWK;
21
use Jose\Object\JWKSet;
22
use Jose\Object\JWKSetInterface;
23
use Jose\Object\JWKSets;
24
use Jose\Object\PublicJWKSet;
25
use Jose\Object\RotatableJWKSet;
26
use Jose\Object\StorableJWK;
27
use Jose\Object\StorableJWKSet;
28
use Jose\Object\X5UJWKSet;
29
use Mdanter\Ecc\Curves\CurveFactory;
30
use Mdanter\Ecc\Curves\NistCurve;
31
use Mdanter\Ecc\EccFactory;
32
use Psr\Cache\CacheItemPoolInterface;
33
34
final class JWKFactory implements JWKFactoryInterface
35
{
36
    /**
37
     * {@inheritdoc}
38
     */
39
    public static function createPublicKeySet(JWKSetInterface $jwkset)
40
    {
41
        return new PublicJWKSet($jwkset);
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public static function createKeySets(array $jwksets = [])
48
    {
49
        return new JWKSets($jwksets);
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public static function createStorableKey($filename, array $parameters)
56
    {
57
        return new StorableJWK($filename, $parameters);
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public static function createRotatableKeySet($filename, array $parameters, $nb_keys)
64
    {
65
        return new RotatableJWKSet($filename, $parameters, $nb_keys);
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public static function createStorableKeySet($filename, array $parameters, $nb_keys)
72
    {
73
        return new StorableJWKSet($filename, $parameters, $nb_keys);
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public static function createKey(array $config)
80
    {
81
        Assertion::keyExists($config, 'kty', 'The key "kty" must be set');
82
        $supported_types = ['RSA' => 'RSA', 'OKP' => 'OKP', 'EC' => 'EC', 'oct' => 'Oct', 'none' => 'None'];
83
        $kty = $config['kty'];
84
        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))));
85
        $method = sprintf('create%sKey', $supported_types[$kty]);
86
87
        return self::$method($config);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93
    public static function createRSAKey(array $values)
94
    {
95
        Assertion::keyExists($values, 'size', 'The key size is not set.');
96
        $size = $values['size'];
97
        unset($values['size']);
98
99
        Assertion::true(0 === $size % 8, 'Invalid key size.');
100
        Assertion::greaterOrEqualThan($size, 384, 'Key length is too short. It needs to be at least 384 bits.');
101
102
        $key = openssl_pkey_new([
103
            'private_key_bits' => $size,
104
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
105
        ]);
106
        openssl_pkey_export($key, $out);
107
        $rsa = new RSAKey($out);
108
        $values = array_merge(
109
            $values,
110
            $rsa->toArray()
111
        );
112
113
        return new JWK($values);
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public static function createECKey(array $values)
120
    {
121
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
122
        $curve = $values['crv'];
123
        if (function_exists('openssl_get_curve_names')) {
124
            $curve_name = self::getOpensslName($curve);
125
            $args = [
126
                'ec_group_name' => $curve_name,
127
                'private_key_type' => OPENSSL_KEYTYPE_EC,
128
            ];
129
            $key = openssl_pkey_new($args);
130
            $res = openssl_pkey_export($key, $out);
131
            Assertion::true($res, 'Unable to create the key');
132
133
            $rsa = new ECKey($out);
134
            $values = array_merge(
135
                $values,
136
                $rsa->toArray()
137
            );
138
            var_dump('OpenSSL youpiiii!');
0 ignored issues
show
Security Debugging Code introduced by
var_dump('OpenSSL youpiiii!'); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
139
140
            return new JWK($values);
141
        } else {
142
            $curve_name = self::getNistName($curve);
143
            $generator = CurveFactory::getGeneratorByName($curve_name);
144
            $private_key = $generator->createPrivateKey();
145
146
            $values = array_merge(
147
                $values,
148
                [
149
                    'kty' => 'EC',
150
                    'crv' => $curve,
151
                    'x'   => self::encodeValue($private_key->getPublicKey()->getPoint()->getX()),
152
                    'y'   => self::encodeValue($private_key->getPublicKey()->getPoint()->getY()),
153
                    'd'   => self::encodeValue($private_key->getSecret()),
154
                ]
155
            );
156
        }
157
158
        return new JWK($values);
159
    }
160
161
    /**
162
     * @param array $values
163
     *
164
     * @return array
165
     */
166
    private static function createECKeyFromNativeFunctions(array $values)
0 ignored issues
show
Unused Code introduced by
The parameter $values is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
167
    {
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public static function createOctKey(array $values)
174
    {
175
        Assertion::keyExists($values, 'size', 'The key size is not set.');
176
        $size = $values['size'];
177
        unset($values['size']);
178
        Assertion::true(0 === $size % 8, 'Invalid key size.');
179
        $values = array_merge(
180
            $values,
181
            [
182
                'kty' => 'oct',
183
                'k'   => Base64Url::encode(random_bytes($size / 8)),
184
            ]
185
        );
186
187
        return new JWK($values);
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193
    public static function createOKPKey(array $values)
194
    {
195
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
196
        $curve = $values['crv'];
197
        switch ($curve) {
198
            case 'X25519':
199
                Assertion::true(function_exists('curve25519_public'), sprintf('Unsupported "%s" curve', $curve));
200
                $d = random_bytes(32);
201
                $x = curve25519_public($d);
202
                break;
203
            case 'Ed25519':
204
                Assertion::true(function_exists('ed25519_publickey'), sprintf('Unsupported "%s" curve', $curve));
205
                $d = random_bytes(32);
206
                $x = ed25519_publickey($d);
207
                break;
208
            default:
209
                throw new \InvalidArgumentException(sprintf('Unsupported "%s" curve', $curve));
210
        }
211
212
        $values = array_merge(
213
            $values,
214
            [
215
                'kty' => 'OKP',
216
                'crv' => $curve,
217
                'x'   => Base64Url::encode($x),
218
                'd'   => Base64Url::encode($d),
219
            ]
220
        );
221
222
        return new JWK($values);
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228
    public static function createNoneKey(array $values)
229
    {
230
        $values = array_merge(
231
            $values,
232
            [
233
                'kty' => 'none',
234
                'alg' => 'none',
235
                'use' => 'sig',
236
            ]
237
        );
238
239
        return new JWK($values);
240
    }
241
242
    /**
243
     * @param string $value
244
     *
245
     * @return string
246
     */
247
    private static function encodeValue($value)
248
    {
249
        $value = gmp_strval($value);
250
251
        return Base64Url::encode(self::convertDecToBin($value));
252
    }
253
254
    /**
255
     * @param string $value
256
     *
257
     * @return string
258
     */
259
    private static function convertDecToBin($value)
260
    {
261
        $adapter = EccFactory::getAdapter();
262
263
        return hex2bin($adapter->decHex($value));
264
    }
265
266
    /**
267
     * @param string $curve
268
     *
269
     * @throws \InvalidArgumentException
270
     *
271
     * @return string
272
     */
273
    private static function getOpensslName($curve)
274
    {
275
        switch ($curve) {
276
            case 'P-256':
277
                return 'secp256k1';
278
            case 'P-384':
279
                return 'secp384r1';
280
            case 'P-521':
281
                return 'secp521r1';
282
            default:
283
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
284
        }
285
    }
286
287
    /**
288
     * @param string $curve
289
     *
290
     * @throws \InvalidArgumentException
291
     *
292
     * @return string
293
     */
294
    private static function getNistName($curve)
295
    {
296
        switch ($curve) {
297
            case 'P-256':
298
                return NistCurve::NAME_P256;
299
            case 'P-384':
300
                return NistCurve::NAME_P384;
301
            case 'P-521':
302
                return NistCurve::NAME_P521;
303
            default:
304
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
305
        }
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311
    public static function createFromValues(array $values)
312
    {
313
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
314
            return new JWKSet($values);
315
        }
316
317
        return new JWK($values);
318
    }
319
320
    /**
321
     * {@inheritdoc}
322
     */
323
    public static function createFromCertificateFile($file, array $additional_values = [])
324
    {
325
        $values = KeyConverter::loadKeyFromCertificateFile($file);
326
        $values = array_merge($values, $additional_values);
327
328
        return new JWK($values);
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public static function createFromCertificate($certificate, array $additional_values = [])
335
    {
336
        $values = KeyConverter::loadKeyFromCertificate($certificate);
337
        $values = array_merge($values, $additional_values);
338
339
        return new JWK($values);
340
    }
341
342
    /**
343
     * {@inheritdoc}
344
     */
345
    public static function createFromX509Resource($res, array $additional_values = [])
346
    {
347
        $values = KeyConverter::loadKeyFromX509Resource($res);
348
        $values = array_merge($values, $additional_values);
349
350
        return new JWK($values);
351
    }
352
353
    /**
354
     * {@inheritdoc}
355
     */
356
    public static function createFromKeyFile($file, $password = null, array $additional_values = [])
357
    {
358
        $values = KeyConverter::loadFromKeyFile($file, $password);
359
        $values = array_merge($values, $additional_values);
360
361
        return new JWK($values);
362
    }
363
364
    /**
365
     * {@inheritdoc}
366
     */
367
    public static function createFromKey($key, $password = null, array $additional_values = [])
368
    {
369
        $values = KeyConverter::loadFromKey($key, $password);
370
        $values = array_merge($values, $additional_values);
371
372
        return new JWK($values);
373
    }
374
375
    /**
376
     * {@inheritdoc}
377
     */
378
    public static function createFromJKU($jku, $allow_unsecured_connection = false, CacheItemPoolInterface $cache = null, $ttl = 86400, $allow_http_connection = false)
379
    {
380
        return new JKUJWKSet($jku, $cache, $ttl, $allow_unsecured_connection, $allow_http_connection);
381
    }
382
383
    /**
384
     * {@inheritdoc}
385
     */
386
    public static function createFromX5U($x5u, $allow_unsecured_connection = false, CacheItemPoolInterface $cache = null, $ttl = 86400, $allow_http_connection = false)
387
    {
388
        return new X5UJWKSet($x5u, $cache, $ttl, $allow_unsecured_connection, $allow_http_connection);
389
    }
390
391
    /**
392
     * {@inheritdoc}
393
     */
394
    public static function createFromX5C(array $x5c, array $additional_values = [])
395
    {
396
        $values = KeyConverter::loadFromX5C($x5c);
397
        $values = array_merge($values, $additional_values);
398
399
        return new JWK($values);
400
    }
401
402
    /**
403
     * {@inheritdoc}
404
     */
405
    public static function createFromKeySet(JWKSetInterface $jwk_set, $key_index)
406
    {
407
        Assertion::integer($key_index);
408
409
        return $jwk_set->getKey($key_index);
410
    }
411
}
412