Failed Conditions
Push — v7 ( 07e0ee...81ed9a )
by Florent
01:51
created

JWKFactory::createFromPKCS12CertificateFile()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.2
c 0
b 0
f 0
cc 4
eloc 5
nc 2
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2014-2017 Spomky-Labs
9
 *
10
 * This software may be modified and distributed under the terms
11
 * of the MIT license.  See the LICENSE file for details.
12
 */
13
14
namespace Jose\Component\KeyManagement;
15
16
use Assert\Assertion;
17
use Base64Url\Base64Url;
18
use Jose\Component\Core\JWK;
19
use Jose\Component\Core\JWKSet;
20
use Jose\Component\KeyManagement\KeyConverter\ECKey;
21
use Jose\Component\KeyManagement\KeyConverter\KeyConverter;
22
use Jose\Component\KeyManagement\KeyConverter\RSAKey;
23
use Mdanter\Ecc\Curves\CurveFactory;
24
use Mdanter\Ecc\Curves\NistCurve;
25
use Mdanter\Ecc\EccFactory;
26
27
final class JWKFactory
28
{
29
    /**
30
     * @param array $values Values to configure the key. Must contain at least the index 'size' with the key size in bits
31
     *
32
     * @return JWK
33
     */
34
    public static function createRSAKey(array $values): JWK
35
    {
36
        Assertion::keyExists($values, 'size', 'The key size is not set.');
37
        $size = $values['size'];
38
        unset($values['size']);
39
40
        Assertion::true(0 === $size % 8, 'Invalid key size.');
41
        Assertion::greaterOrEqualThan($size, 384, 'Key length is too short. It needs to be at least 384 bits.');
42
43
        $key = openssl_pkey_new([
44
            'private_key_bits' => $size,
45
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
46
        ]);
47
        openssl_pkey_export($key, $out);
48
        $rsa = RSAKey::createFromPEM($out);
49
        $values = array_merge(
50
            $values,
51
            $rsa->toArray()
52
        );
53
54
        return JWK::create($values);
55
    }
56
57
    /**
58
     * @param array $values Values to configure the key. Must contain at least the index 'crv' with the curve
59
     *
60
     * @return JWK
61
     */
62
    public static function createECKey(array $values): JWK
63
    {
64
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
65
        $curve = $values['crv'];
66
        if (function_exists('openssl_get_curve_names')) {
67
            $args = [
68
                'curve_name' => self::getOpensslName($curve),
69
                'private_key_type' => OPENSSL_KEYTYPE_EC,
70
            ];
71
            $key = openssl_pkey_new($args);
72
            $res = openssl_pkey_export($key, $out);
73
            Assertion::true($res, 'Unable to create the key');
74
75
            $rsa = ECKey::createFromPEM($out);
76
            $values = array_merge(
77
                $values,
78
                $rsa->toArray()
79
            );
80
81
            return JWK::create($values);
82
        } else {
83
            $curve_name = self::getNistName($curve);
84
            $generator = CurveFactory::getGeneratorByName($curve_name);
85
            $private_key = $generator->createPrivateKey();
86
87
            $values = array_merge(
88
                $values,
89
                [
90
                    'kty' => 'EC',
91
                    'crv' => $curve,
92
                    'x' => self::encodeValue($private_key->getPublicKey()->getPoint()->getX()),
93
                    'y' => self::encodeValue($private_key->getPublicKey()->getPoint()->getY()),
94
                    'd' => self::encodeValue($private_key->getSecret()),
95
                ]
96
            );
97
        }
98
99
        return JWK::create($values);
100
    }
101
102
    /**
103
     * @param array $values Values to configure the key. Must contain at least the index 'size' with the key size in bits
104
     *
105
     * @return JWK
106
     */
107
    public static function createOctKey(array $values): JWK
108
    {
109
        Assertion::keyExists($values, 'size', 'The key size is not set.');
110
        $size = $values['size'];
111
        unset($values['size']);
112
        Assertion::true(0 === $size % 8, 'Invalid key size.');
113
        $values = array_merge(
114
            $values,
115
            [
116
                'kty' => 'oct',
117
                'k' => Base64Url::encode(random_bytes($size / 8)),
118
            ]
119
        );
120
121
        return JWK::create($values);
122
    }
123
124
    /**
125
     * @param array $values Values to configure the key. Must contain at least the index 'crv' with the curve
126
     *
127
     * @return JWK
128
     */
129
    public static function createOKPKey(array $values): JWK
130
    {
131
        Assertion::keyExists($values, 'crv', 'The curve is not set.');
132
        $curve = $values['crv'];
133
        switch ($curve) {
134
            case 'X25519':
135
                $d = sodium_randombytes_buf(\Sodium\CRYPTO_BOX_SEEDBYTES);
136
                $x = sodium_crypto_scalarmult_base($d);
137
                break;
138
            case 'Ed25519':
139
                $d = sodium_randombytes_buf(\Sodium\CRYPTO_SIGN_SEEDBYTES);
140
                $keyPair = sodium_crypto_sign_seed_keypair($d);
141
                $x = sodium_crypto_sign_publickey($keyPair);
142
                break;
143
            default:
144
                throw new \InvalidArgumentException(sprintf('Unsupported "%s" curve', $curve));
145
        }
146
147
        $values = array_merge(
148
            $values,
149
            [
150
                'kty' => 'OKP',
151
                'crv' => $curve,
152
                'x' => Base64Url::encode($x),
153
                'd' => Base64Url::encode($d),
154
            ]
155
        );
156
157
        return JWK::create($values);
158
    }
159
160
    /**
161
     * @param array $values values to configure the key
162
     *
163
     * @return JWK
164
     */
165
    public static function createNoneKey(array $values): JWK
166
    {
167
        $values = array_merge(
168
            $values,
169
            [
170
                'kty' => 'none',
171
                'alg' => 'none',
172
                'use' => 'sig',
173
            ]
174
        );
175
176
        return JWK::create($values);
177
    }
178
179
    /**
180
     * @param \GMP $value
181
     *
182
     * @return string
183
     */
184
    private static function encodeValue(\GMP $value): string
185
    {
186
        $value = gmp_strval($value);
187
188
        return Base64Url::encode(self::convertDecToBin($value));
189
    }
190
191
    /**
192
     * @param string $value
193
     *
194
     * @return string
195
     */
196
    private static function convertDecToBin(string $value): string
197
    {
198
        $adapter = EccFactory::getAdapter();
199
200
        return hex2bin($adapter->decHex($value));
201
    }
202
203
    /**
204
     * @param string $curve
205
     *
206
     * @throws \InvalidArgumentException
207
     *
208
     * @return string
209
     */
210
    private static function getOpensslName(string $curve): string
211
    {
212
        switch ($curve) {
213
            case 'P-256':
214
                return 'prime256v1';
215
            case 'P-384':
216
                return 'secp384r1';
217
            case 'P-521':
218
                return 'secp521r1';
219
            default:
220
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
221
        }
222
    }
223
224
    /**
225
     * @param string $curve
226
     *
227
     * @throws \InvalidArgumentException
228
     *
229
     * @return string
230
     */
231
    private static function getNistName(string $curve): string
232
    {
233
        switch ($curve) {
234
            case 'P-256':
235
                return NistCurve::NAME_P256;
236
            case 'P-384':
237
                return NistCurve::NAME_P384;
238
            case 'P-521':
239
                return NistCurve::NAME_P521;
240
            default:
241
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
242
        }
243
    }
244
245
    /**
246
     * @param array $values
247
     *
248
     * @return JWK|JWKSet
249
     */
250
    public static function createFromValues(array $values)
251
    {
252
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
253
            return JWKSet::createFromKeyData($values);
254
        }
255
256
        return JWK::create($values);
257
    }
258
259
    /**
260
     * @param string $file
261
     * @param array  $additional_values
262
     *
263
     * @return JWK
264
     */
265
    public static function createFromCertificateFile(string $file, array $additional_values = []): JWK
266
    {
267
        $values = KeyConverter::loadKeyFromCertificateFile($file);
268
        $values = array_merge($values, $additional_values);
269
270
        return JWK::create($values);
271
    }
272
273
    /**
274
     * @param string      $file
275
     * @param null|string $secret
276
     * @param array       $additional_values
277
     *
278
     * @return JWK
279
     */
280
    public static function createFromPKCS12CertificateFile(string $file, ?string $secret, array $additional_values = []): JWK
281
    {
282
        $res = openssl_pkcs12_read(file_get_contents($file),$certs, $secret);
283
        if (false === $res || !is_array($certs) || !array_key_exists('pkey', $certs)) {
284
            throw new \RuntimeException('Unable to load the certificates.');
285
        }
286
287
        return JWKFactory::createFromKey($certs['pkey'], null, $additional_values);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
288
    }
289
290
    /**
291
     * @param string $certificate
292
     * @param array  $additional_values
293
     *
294
     * @return JWK
295
     */
296
    public static function createFromCertificate(string $certificate, array $additional_values = []): JWK
297
    {
298
        $values = KeyConverter::loadKeyFromCertificate($certificate);
299
        $values = array_merge($values, $additional_values);
300
301
        return JWK::create($values);
302
    }
303
304
    /**
305
     * @param resource $res
306
     * @param array    $additional_values
307
     *
308
     * @return JWK
309
     */
310
    public static function createFromX509Resource($res, array $additional_values = []): JWK
311
    {
312
        $values = KeyConverter::loadKeyFromX509Resource($res);
313
        $values = array_merge($values, $additional_values);
314
315
        return JWK::create($values);
316
    }
317
318
    /**
319
     * @param string      $file
320
     * @param null|string $password
321
     * @param array       $additional_values
322
     *
323
     * @return JWK
324
     */
325
    public static function createFromKeyFile(string $file, ?string $password = null, array $additional_values = []): JWK
326
    {
327
        $values = KeyConverter::loadFromKeyFile($file, $password);
328
        $values = array_merge($values, $additional_values);
329
330
        return JWK::create($values);
331
    }
332
333
    /**
334
     * @param string      $key
335
     * @param null|string $password
336
     * @param array       $additional_values
337
     *
338
     * @return JWK
339
     */
340
    public static function createFromKey(string $key, ?string $password = null, array $additional_values = []): JWK
341
    {
342
        $values = KeyConverter::loadFromKey($key, $password);
343
        $values = array_merge($values, $additional_values);
344
345
        return JWK::create($values);
346
    }
347
348
    /**
349
     * @param array $x5c
350
     * @param array $additional_values
351
     *
352
     * @return JWK
353
     */
354
    public static function createFromX5C(array $x5c, array $additional_values = []): JWK
355
    {
356
        $values = KeyConverter::loadFromX5C($x5c);
357
        $values = array_merge($values, $additional_values);
358
359
        return JWK::create($values);
360
    }
361
362
    /**
363
     * @param JWKSet $jwk_set
364
     * @param int    $key_index
365
     *
366
     * @return JWK
367
     */
368
    public static function createFromKeySet(JWKSet $jwk_set, int $key_index): JWK
369
    {
370
        Assertion::integer($key_index);
371
372
        return $jwk_set->getKey($key_index);
373
    }
374
}
375