Failed Conditions
Pull Request — master (#154)
by Jeroen
02:37
created

JWKFactory::getOpensslName()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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