PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / web-token / jwt-core / Util / ECKey.php

ECKey.php in Defender Security – Malware Scanner, Login Security & Firewall 5.8.1, at vendor/web-token/jwt-core/Util/ECKey.php

367 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 /*
6 * The MIT License (MIT)
7 *
8 * Copyright (c) 2014-2020 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\Core\Util;
15
16 use Base64Url\Base64Url;
17 use function extension_loaded;
18 use InvalidArgumentException;
19 use function is_array;
20 use Jose\Component\Core\JWK;
21 use RuntimeException;
22
23 /**
24 * @internal
25 */
26 class ECKey
27 {
28 public static function convertToPEM(JWK $jwk): string
29 {
30 if ($jwk->has('d')) {
31 return self::convertPrivateKeyToPEM($jwk);
32 }
33
34 return self::convertPublicKeyToPEM($jwk);
35 }
36
37 /**
38 * @throws InvalidArgumentException if the curve is not supported
39 */
40 public static function convertPublicKeyToPEM(JWK $jwk): string
41 {
42 switch ($jwk->get('crv')) {
43 case 'P-256':
44 $der = self::p256PublicKey();
45
46 break;
47
48 case 'secp256k1':
49 $der = self::p256KPublicKey();
50
51 break;
52
53 case 'P-384':
54 $der = self::p384PublicKey();
55
56 break;
57
58 case 'P-521':
59 $der = self::p521PublicKey();
60
61 break;
62
63 default:
64 throw new InvalidArgumentException('Unsupported curve.');
65 }
66 $der .= self::getKey($jwk);
67 $pem = '-----BEGIN PUBLIC KEY-----'.PHP_EOL;
68 $pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
69 $pem .= '-----END PUBLIC KEY-----'.PHP_EOL;
70
71 return $pem;
72 }
73
74 /**
75 * @throws InvalidArgumentException if the curve is not supported
76 */
77 public static function convertPrivateKeyToPEM(JWK $jwk): string
78 {
79 switch ($jwk->get('crv')) {
80 case 'P-256':
81 $der = self::p256PrivateKey($jwk);
82
83 break;
84
85 case 'secp256k1':
86 $der = self::p256KPrivateKey($jwk);
87
88 break;
89
90 case 'P-384':
91 $der = self::p384PrivateKey($jwk);
92
93 break;
94
95 case 'P-521':
96 $der = self::p521PrivateKey($jwk);
97
98 break;
99
100 default:
101 throw new InvalidArgumentException('Unsupported curve.');
102 }
103 $der .= self::getKey($jwk);
104 $pem = '-----BEGIN EC PRIVATE KEY-----'.PHP_EOL;
105 $pem .= chunk_split(base64_encode($der), 64, PHP_EOL);
106 $pem .= '-----END EC PRIVATE KEY-----'.PHP_EOL;
107
108 return $pem;
109 }
110
111 /**
112 * Creates a EC key with the given curve and additional values.
113 *
114 * @param string $curve The curve
115 * @param array $values values to configure the key
116 */
117 public static function createECKey(string $curve, array $values = []): JWK
118 {
119 $jwk = self::createECKeyUsingOpenSSL($curve);
120 $values = array_merge($values, $jwk);
121
122 return new JWK($values);
123 }
124
125 /**
126 * @throws InvalidArgumentException if the curve is not supported
127 */
128 private static function getNistCurveSize(string $curve): int
129 {
130 switch ($curve) {
131 case 'P-256':
132 case 'secp256k1':
133 return 256;
134
135 case 'P-384':
136 return 384;
137
138 case 'P-521':
139 return 521;
140
141 default:
142 throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
143 }
144 }
145
146 /**
147 * @throws RuntimeException if the extension OpenSSL is not available
148 * @throws RuntimeException if the key cannot be created
149 */
150 private static function createECKeyUsingOpenSSL(string $curve): array
151 {
152 if (!extension_loaded('openssl')) {
153 throw new RuntimeException('Please install the OpenSSL extension');
154 }
155 $key = openssl_pkey_new([
156 'curve_name' => self::getOpensslCurveName($curve),
157 'private_key_type' => OPENSSL_KEYTYPE_EC,
158 ]);
159 if (false === $key) {
160 throw new RuntimeException('Unable to create the key');
161 }
162 $result = openssl_pkey_export($key, $out);
163 if (false === $result) {
164 throw new RuntimeException('Unable to create the key');
165 }
166 $res = openssl_pkey_get_private($out);
167 if (false === $res) {
168 throw new RuntimeException('Unable to create the key');
169 }
170 $details = openssl_pkey_get_details($res);
171 if (false === $details) {
172 throw new InvalidArgumentException('Unable to get the key details');
173 }
174 $nistCurveSize = self::getNistCurveSize($curve);
175
176 return [
177 'kty' => 'EC',
178 'crv' => $curve,
179 'd' => Base64Url::encode(str_pad($details['ec']['d'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
180 'x' => Base64Url::encode(str_pad($details['ec']['x'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
181 'y' => Base64Url::encode(str_pad($details['ec']['y'], (int) ceil($nistCurveSize / 8), "\0", STR_PAD_LEFT)),
182 ];
183 }
184
185 /**
186 * @throws InvalidArgumentException if the curve is not supported
187 */
188 private static function getOpensslCurveName(string $curve): string
189 {
190 switch ($curve) {
191 case 'P-256':
192 return 'prime256v1';
193
194 case 'secp256k1':
195 return 'secp256k1';
196
197 case 'P-384':
198 return 'secp384r1';
199
200 case 'P-521':
201 return 'secp521r1';
202
203 default:
204 throw new InvalidArgumentException(sprintf('The curve "%s" is not supported.', $curve));
205 }
206 }
207
208 private static function p256PublicKey(): string
209 {
210 return pack(
211 'H*',
212 '3059' // SEQUENCE, length 89
213 .'3013' // SEQUENCE, length 19
214 .'0607' // OID, length 7
215 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
216 .'0608' // OID, length 8
217 .'2a8648ce3d030107' // 1.2.840.10045.3.1.7 = P-256 Curve
218 .'0342' // BIT STRING, length 66
219 .'00' // prepend with NUL - pubkey will follow
220 );
221 }
222
223 private static function p256KPublicKey(): string
224 {
225 return pack(
226 'H*',
227 '3056' // SEQUENCE, length 86
228 .'3010' // SEQUENCE, length 16
229 .'0607' // OID, length 7
230 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
231 .'0605' // OID, length 8
232 .'2B8104000A' // 1.3.132.0.10 secp256k1
233 .'0342' // BIT STRING, length 66
234 .'00' // prepend with NUL - pubkey will follow
235 );
236 }
237
238 private static function p384PublicKey(): string
239 {
240 return pack(
241 'H*',
242 '3076' // SEQUENCE, length 118
243 .'3010' // SEQUENCE, length 16
244 .'0607' // OID, length 7
245 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
246 .'0605' // OID, length 5
247 .'2b81040022' // 1.3.132.0.34 = P-384 Curve
248 .'0362' // BIT STRING, length 98
249 .'00' // prepend with NUL - pubkey will follow
250 );
251 }
252
253 private static function p521PublicKey(): string
254 {
255 return pack(
256 'H*',
257 '30819b' // SEQUENCE, length 154
258 .'3010' // SEQUENCE, length 16
259 .'0607' // OID, length 7
260 .'2a8648ce3d0201' // 1.2.840.10045.2.1 = EC Public Key
261 .'0605' // OID, length 5
262 .'2b81040023' // 1.3.132.0.35 = P-521 Curve
263 .'038186' // BIT STRING, length 134
264 .'00' // prepend with NUL - pubkey will follow
265 );
266 }
267
268 private static function p256PrivateKey(JWK $jwk): string
269 {
270 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT));
271 if (!is_array($d) || !isset($d[1])) {
272 throw new InvalidArgumentException('Unable to get the private key');
273 }
274
275 return pack(
276 'H*',
277 '3077' // SEQUENCE, length 87+length($d)=32
278 .'020101' // INTEGER, 1
279 .'0420' // OCTET STRING, length($d) = 32
280 .$d[1]
281 .'a00a' // TAGGED OBJECT #0, length 10
282 .'0608' // OID, length 8
283 .'2a8648ce3d030107' // 1.3.132.0.34 = P-256 Curve
284 .'a144' // TAGGED OBJECT #1, length 68
285 .'0342' // BIT STRING, length 66
286 .'00' // prepend with NUL - pubkey will follow
287 );
288 }
289
290 private static function p256KPrivateKey(JWK $jwk): string
291 {
292 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 32, "\0", STR_PAD_LEFT));
293 if (!is_array($d) || !isset($d[1])) {
294 throw new InvalidArgumentException('Unable to get the private key');
295 }
296
297 return pack(
298 'H*',
299 '3074' // SEQUENCE, length 84+length($d)=32
300 .'020101' // INTEGER, 1
301 .'0420' // OCTET STRING, length($d) = 32
302 .$d[1]
303 .'a007' // TAGGED OBJECT #0, length 7
304 .'0605' // OID, length 5
305 .'2b8104000a' // 1.3.132.0.10 secp256k1
306 .'a144' // TAGGED OBJECT #1, length 68
307 .'0342' // BIT STRING, length 66
308 .'00' // prepend with NUL - pubkey will follow
309 );
310 }
311
312 private static function p384PrivateKey(JWK $jwk): string
313 {
314 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 48, "\0", STR_PAD_LEFT));
315 if (!is_array($d) || !isset($d[1])) {
316 throw new InvalidArgumentException('Unable to get the private key');
317 }
318
319 return pack(
320 'H*',
321 '3081a4' // SEQUENCE, length 116 + length($d)=48
322 .'020101' // INTEGER, 1
323 .'0430' // OCTET STRING, length($d) = 30
324 .$d[1]
325 .'a007' // TAGGED OBJECT #0, length 7
326 .'0605' // OID, length 5
327 .'2b81040022' // 1.3.132.0.34 = P-384 Curve
328 .'a164' // TAGGED OBJECT #1, length 100
329 .'0362' // BIT STRING, length 98
330 .'00' // prepend with NUL - pubkey will follow
331 );
332 }
333
334 private static function p521PrivateKey(JWK $jwk): string
335 {
336 $d = unpack('H*', str_pad(Base64Url::decode($jwk->get('d')), 66, "\0", STR_PAD_LEFT));
337 if (!is_array($d) || !isset($d[1])) {
338 throw new InvalidArgumentException('Unable to get the private key');
339 }
340
341 return pack(
342 'H*',
343 '3081dc' // SEQUENCE, length 154 + length($d)=66
344 .'020101' // INTEGER, 1
345 .'0442' // OCTET STRING, length(d) = 66
346 .$d[1]
347 .'a007' // TAGGED OBJECT #0, length 7
348 .'0605' // OID, length 5
349 .'2b81040023' // 1.3.132.0.35 = P-521 Curve
350 .'a18189' // TAGGED OBJECT #1, length 137
351 .'038186' // BIT STRING, length 134
352 .'00' // prepend with NUL - pubkey will follow
353 );
354 }
355
356 private static function getKey(JWK $jwk): string
357 {
358 $nistCurveSize = self::getNistCurveSize($jwk->get('crv'));
359 $length = (int) ceil($nistCurveSize / 8);
360
361 return
362 "\04"
363 .str_pad(Base64Url::decode($jwk->get('x')), $length, "\0", STR_PAD_LEFT)
364 .str_pad(Base64Url::decode($jwk->get('y')), $length, "\0", STR_PAD_LEFT);
365 }
366 }
367