Failed Conditions
Push — v7 ( e9e51b...d9c0af )
by Florent
03:20
created

JWKFactory   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 6
dl 0
loc 283
rs 10
c 0
b 0
f 0

15 Methods

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