Failed Conditions
Pull Request — master (#327)
by
unknown
02:10
created

JWKFactory::createRSAKey()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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