Failed Conditions
Push — v7 ( ca0cc9...2f79b4 )
by Florent
01:58
created

JWKFactory::createFromString()   A

Complexity

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