Failed Conditions
Pull Request — master (#151)
by Florent
09:47 queued 07:38
created

JWKFactory::createECKey()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 39
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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