Failed Conditions
Push — master ( dedff1...43e85c )
by Florent
04:53
created

JWKFactory::createFromKeyFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
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 Base64Url\Base64Url;
17
use Jose\Component\Core\JWK;
18
use Jose\Component\Core\JWKSet;
19
use Jose\Component\Core\Util\Ecc\NistCurve;
20
use Jose\Component\KeyManagement\KeyConverter\KeyConverter;
21
use Jose\Component\KeyManagement\KeyConverter\RSAKey;
22
23
/**
24
 * Class JWKFactory.
25
 */
26
final class JWKFactory
27
{
28
    /**
29
     * @param int   $size   The key size in bits
30
     * @param array $values values to configure the key
31
     *
32
     * @return JWK
33
     */
34
    public static function createRSAKey(int $size, array $values = []): JWK
35
    {
36
        if (0 !== $size % 8) {
37
            throw new \InvalidArgumentException('Invalid key size.');
38
        }
39
40
        if (384 > $size) {
41
            throw new \InvalidArgumentException('Key length is too short. It needs to be at least 384 bits.');
42
        }
43
44
        $key = openssl_pkey_new([
45
            'private_key_bits' => $size,
46
            'private_key_type' => OPENSSL_KEYTYPE_RSA,
47
        ]);
48
        openssl_pkey_export($key, $out);
49
        $rsa = RSAKey::createFromPEM($out);
50
        $values = array_merge(
51
            $values,
52
            $rsa->toArray()
53
        );
54
55
        return JWK::create($values);
56
    }
57
58
    /**
59
     * @param string $curve  The curve
60
     * @param array  $values values to configure the key
61
     *
62
     * @return JWK
63
     */
64
    public static function createECKey(string $curve, array $values = []): JWK
65
    {
66
        switch ($curve) {
67
            case 'P-256':
68
                $nistCurve = NistCurve::curve256();
69
70
                break;
71
            case 'P-384':
72
                $nistCurve = NistCurve::curve384();
73
74
                break;
75
            case 'P-521':
76
                $nistCurve = NistCurve::curve521();
77
78
                break;
79
            default:
80
                throw new \InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
81
        }
82
83
        $privateKey = $nistCurve->createPrivateKey();
84
        $publicKey = $nistCurve->createPublicKey($privateKey);
85
86
        $values = array_merge(
87
            $values,
88
            [
89
                'kty' => 'EC',
90
                'crv' => $curve,
91
                'd' => Base64Url::encode(gmp_export($privateKey->getSecret())),
92
                'x' => Base64Url::encode(gmp_export($publicKey->getPoint()->getX())),
93
                'y' => Base64Url::encode(gmp_export($publicKey->getPoint()->getY())),
94
            ]
95
        );
96
97
        return JWK::create($values);
98
    }
99
100
    /**
101
     * @param int   $size   The key size in bits
102
     * @param array $values values to configure the key
103
     *
104
     * @return JWK
105
     */
106
    public static function createOctKey(int $size, array $values = []): JWK
107
    {
108
        if (0 !== $size % 8) {
109
            throw new \InvalidArgumentException('Invalid key size.');
110
        }
111
        $values = array_merge(
112
            $values,
113
            [
114
                'kty' => 'oct',
115
                'k' => Base64Url::encode(random_bytes($size / 8)),
116
            ]
117
        );
118
119
        return JWK::create($values);
120
    }
121
122
    /**
123
     * @param string $curve  The curve
124
     * @param array  $values values to configure the key
125
     *
126
     * @return JWK
127
     */
128
    public static function createOKPKey(string $curve, array $values = []): JWK
129
    {
130
        switch ($curve) {
131
            case 'X25519':
132
                $d = sodium_randombytes_buf(\Sodium\CRYPTO_BOX_SEEDBYTES);
133
                $x = sodium_crypto_scalarmult_base($d);
134
135
                break;
136
            case 'Ed25519':
137
                $d = sodium_randombytes_buf(\Sodium\CRYPTO_SIGN_SEEDBYTES);
138
                $keyPair = sodium_crypto_sign_seed_keypair($d);
139
                $x = sodium_crypto_sign_publickey($keyPair);
140
141
                break;
142
            default:
143
                throw new \InvalidArgumentException(sprintf('Unsupported "%s" curve', $curve));
144
        }
145
146
        $values = array_merge(
147
            $values,
148
            [
149
                'kty' => 'OKP',
150
                'crv' => $curve,
151
                'x' => Base64Url::encode($x),
152
                'd' => Base64Url::encode($d),
153
            ]
154
        );
155
156
        return JWK::create($values);
157
    }
158
159
    /**
160
     * @param array $values values to configure the key
161
     *
162
     * @return JWK
163
     */
164
    public static function createNoneKey(array $values = []): JWK
165
    {
166
        $values = array_merge(
167
            $values,
168
            [
169
                'kty' => 'none',
170
                'alg' => 'none',
171
                'use' => 'sig',
172
            ]
173
        );
174
175
        return JWK::create($values);
176
    }
177
178
    /**
179
     * @param string $value
180
     *
181
     * @return JWK|JWKSet
182
     */
183
    public static function createFromString(string $value)
184
    {
185
        $json = json_decode($value, true);
186
        if (!is_array($json)) {
187
            throw new \InvalidArgumentException('Invalid key or key set.');
188
        }
189
190
        return self::createFromValues($json);
191
    }
192
193
    /**
194
     * @param array $values
195
     *
196
     * @return JWK|JWKSet
197
     */
198
    public static function createFromValues(array $values)
199
    {
200
        if (array_key_exists('keys', $values) && is_array($values['keys'])) {
201
            return JWKSet::createFromKeyData($values);
202
        }
203
204
        return JWK::create($values);
205
    }
206
207
    /**
208
     * @param string $file
209
     * @param array  $additional_values
210
     *
211
     * @return JWK
212
     */
213
    public static function createFromCertificateFile(string $file, array $additional_values = []): JWK
214
    {
215
        $values = KeyConverter::loadKeyFromCertificateFile($file);
216
        $values = array_merge($values, $additional_values);
217
218
        return JWK::create($values);
219
    }
220
221
    /**
222
     * @param JWKSet     $jwkset
223
     * @param int|string $index
224
     *
225
     * @return JWK
226
     */
227
    public static function createFromKeySet(JWKSet $jwkset, $index): JWK
228
    {
229
        return $jwkset->get($index);
230
    }
231
232
    /**
233
     * @param string      $file
234
     * @param null|string $secret
235
     * @param array       $additional_values
236
     *
237
     * @return JWK
238
     */
239
    public static function createFromPKCS12CertificateFile(string $file, ?string $secret = '', array $additional_values = []): JWK
240
    {
241
        $res = openssl_pkcs12_read(file_get_contents($file), $certs, $secret);
242
        if (false === $res || !is_array($certs) || !array_key_exists('pkey', $certs)) {
243
            throw new \RuntimeException('Unable to load the certificates.');
244
        }
245
246
        return self::createFromKey($certs['pkey'], null, $additional_values);
247
    }
248
249
    /**
250
     * @param string $certificate
251
     * @param array  $additional_values
252
     *
253
     * @return JWK
254
     */
255
    public static function createFromCertificate(string $certificate, array $additional_values = []): JWK
256
    {
257
        $values = KeyConverter::loadKeyFromCertificate($certificate);
258
        $values = array_merge($values, $additional_values);
259
260
        return JWK::create($values);
261
    }
262
263
    /**
264
     * @param resource $res
265
     * @param array    $additional_values
266
     *
267
     * @return JWK
268
     */
269
    public static function createFromX509Resource($res, array $additional_values = []): JWK
270
    {
271
        $values = KeyConverter::loadKeyFromX509Resource($res);
272
        $values = array_merge($values, $additional_values);
273
274
        return JWK::create($values);
275
    }
276
277
    /**
278
     * @param string      $file
279
     * @param null|string $password
280
     * @param array       $additional_values
281
     *
282
     * @return JWK
283
     */
284
    public static function createFromKeyFile(string $file, ?string $password = null, array $additional_values = []): JWK
285
    {
286
        $values = KeyConverter::loadFromKeyFile($file, $password);
287
        $values = array_merge($values, $additional_values);
288
289
        return JWK::create($values);
290
    }
291
292
    /**
293
     * @param string      $key
294
     * @param null|string $password
295
     * @param array       $additional_values
296
     *
297
     * @return JWK
298
     */
299
    public static function createFromKey(string $key, ?string $password = null, array $additional_values = []): JWK
300
    {
301
        $values = KeyConverter::loadFromKey($key, $password);
302
        $values = array_merge($values, $additional_values);
303
304
        return JWK::create($values);
305
    }
306
307
    /**
308
     * @param array $x5c
309
     * @param array $additional_values
310
     *
311
     * @return JWK
312
     */
313
    public static function createFromX5C(array $x5c, array $additional_values = []): JWK
314
    {
315
        $values = KeyConverter::loadFromX5C($x5c);
316
        $values = array_merge($values, $additional_values);
317
318
        return JWK::create($values);
319
    }
320
}
321