PluginProbe
Media Cloud Sync / trunk
Media Cloud Sync vtrunk
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / google / firebase / php-jwt / src / JWT.php

JWT.php in Media Cloud Sync trunk, at includes/sdk/google/firebase/php-jwt/src/JWT.php

643 lines 26.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\GCP\Firebase\JWT;
4
5 use ArrayAccess;
6 use DateTime;
7 use DomainException;
8 use Exception;
9 use InvalidArgumentException;
10 use OpenSSLAsymmetricKey;
11 use OpenSSLCertificate;
12 use stdClass;
13 use UnexpectedValueException;
14 /**
15 * JSON Web Token implementation, based on this spec:
16 * https://tools.ietf.org/html/rfc7519
17 *
18 * PHP version 5
19 *
20 * @category Authentication
21 * @package Authentication_JWT
22 * @author Neuman Vong <neuman@twilio.com>
23 * @author Anant Narayanan <anant@php.net>
24 * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
25 * @link https://github.com/firebase/php-jwt
26 */
27 class JWT
28 {
29 private const ASN1_INTEGER = 0x2;
30 private const ASN1_SEQUENCE = 0x10;
31 private const ASN1_BIT_STRING = 0x3;
32 private const RSA_KEY_MIN_LENGTH = 2048;
33 /**
34 * When checking nbf, iat or expiration times,
35 * we want to provide some extra leeway time to
36 * account for clock skew.
37 *
38 * @var int
39 */
40 public static $leeway = 0;
41 /**
42 * Allow the current timestamp to be specified.
43 * Useful for fixing a value within unit testing.
44 * Will default to PHP time() value if null.
45 *
46 * @var ?int
47 */
48 public static $timestamp = null;
49 /**
50 * @var array<string, string[]>
51 */
52 public static $supported_algs = ['ES384' => ['openssl', 'SHA384'], 'ES256' => ['openssl', 'SHA256'], 'ES256K' => ['openssl', 'SHA256'], 'HS256' => ['hash_hmac', 'SHA256'], 'HS384' => ['hash_hmac', 'SHA384'], 'HS512' => ['hash_hmac', 'SHA512'], 'RS256' => ['openssl', 'SHA256'], 'RS384' => ['openssl', 'SHA384'], 'RS512' => ['openssl', 'SHA512'], 'EdDSA' => ['sodium_crypto', 'EdDSA']];
53 /**
54 * Decodes a JWT string into a PHP object.
55 *
56 * @param string $jwt The JWT
57 * @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray The Key or associative array of key IDs
58 * (kid) to Key objects.
59 * If the algorithm used is asymmetric, this is
60 * the public key.
61 * Each Key object contains an algorithm and
62 * matching key.
63 * Supported algorithms are 'ES384','ES256',
64 * 'HS256', 'HS384', 'HS512', 'RS256', 'RS384'
65 * and 'RS512'.
66 * @param stdClass $headers Optional. Populates stdClass with headers.
67 *
68 * @return stdClass The JWT's payload as a PHP object
69 *
70 * @throws InvalidArgumentException Provided key/key-array was empty or malformed
71 * @throws DomainException Provided JWT is malformed
72 * @throws UnexpectedValueException Provided JWT was invalid
73 * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
74 * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
75 * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
76 * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
77 *
78 * @uses jsonDecode
79 * @uses urlsafeB64Decode
80 */
81 public static function decode(string $jwt, #[\SensitiveParameter] $keyOrKeyArray, ?stdClass &$headers = null) : stdClass
82 {
83 // Validate JWT
84 $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
85 if (empty($keyOrKeyArray)) {
86 throw new InvalidArgumentException('Key may not be empty');
87 }
88 $tks = \explode('.', $jwt);
89 if (\count($tks) !== 3) {
90 throw new UnexpectedValueException('Wrong number of segments');
91 }
92 list($headb64, $bodyb64, $cryptob64) = $tks;
93 $headerRaw = static::urlsafeB64Decode($headb64);
94 if (null === ($header = static::jsonDecode($headerRaw))) {
95 throw new UnexpectedValueException('Invalid header encoding');
96 }
97 if ($headers !== null) {
98 $headers = $header;
99 }
100 $payloadRaw = static::urlsafeB64Decode($bodyb64);
101 if (null === ($payload = static::jsonDecode($payloadRaw))) {
102 throw new UnexpectedValueException('Invalid claims encoding');
103 }
104 if (\is_array($payload)) {
105 // prevent PHP Fatal Error in edge-cases when payload is empty array
106 $payload = (object) $payload;
107 }
108 if (!$payload instanceof stdClass) {
109 throw new UnexpectedValueException('Payload must be a JSON object');
110 }
111 if (isset($payload->iat) && !\is_numeric($payload->iat)) {
112 throw new UnexpectedValueException('Payload iat must be a number');
113 }
114 if (isset($payload->nbf) && !\is_numeric($payload->nbf)) {
115 throw new UnexpectedValueException('Payload nbf must be a number');
116 }
117 if (isset($payload->exp) && !\is_numeric($payload->exp)) {
118 throw new UnexpectedValueException('Payload exp must be a number');
119 }
120 $sig = static::urlsafeB64Decode($cryptob64);
121 if (empty($header->alg)) {
122 throw new UnexpectedValueException('Empty algorithm');
123 }
124 if (empty(static::$supported_algs[$header->alg])) {
125 throw new UnexpectedValueException('Algorithm not supported');
126 }
127 $key = self::getKey($keyOrKeyArray, \property_exists($header, 'kid') ? $header->kid : null);
128 // Check the algorithm
129 if (!self::constantTimeEquals($key->getAlgorithm(), $header->alg)) {
130 // See issue #351
131 throw new UnexpectedValueException('Incorrect key for this algorithm');
132 }
133 if (\in_array($header->alg, ['ES256', 'ES256K', 'ES384'], \true)) {
134 // OpenSSL expects an ASN.1 DER sequence for ES256/ES256K/ES384 signatures
135 $sig = self::signatureToDER($sig);
136 }
137 if (!self::verify("{$headb64}.{$bodyb64}", $sig, $key->getKeyMaterial(), $header->alg)) {
138 throw new SignatureInvalidException('Signature verification failed');
139 }
140 // Check the nbf if it is defined. This is the time that the
141 // token can actually be used. If it's not yet that time, abort.
142 if (isset($payload->nbf) && \floor($payload->nbf) > $timestamp + static::$leeway) {
143 $ex = new BeforeValidException('Cannot handle token with nbf prior to ' . \date(DateTime::ATOM, (int) \floor($payload->nbf)));
144 $ex->setPayload($payload);
145 throw $ex;
146 }
147 // Check that this token has been created before 'now'. This prevents
148 // using tokens that have been created for later use (and haven't
149 // correctly used the nbf claim).
150 if (!isset($payload->nbf) && isset($payload->iat) && \floor($payload->iat) > $timestamp + static::$leeway) {
151 $ex = new BeforeValidException('Cannot handle token with iat prior to ' . \date(DateTime::ATOM, (int) \floor($payload->iat)));
152 $ex->setPayload($payload);
153 throw $ex;
154 }
155 // Check if this token has expired.
156 if (isset($payload->exp) && $timestamp - static::$leeway >= $payload->exp) {
157 $ex = new ExpiredException('Expired token');
158 $ex->setPayload($payload);
159 $ex->setTimestamp($timestamp);
160 throw $ex;
161 }
162 return $payload;
163 }
164 /**
165 * Converts and signs a PHP array into a JWT string.
166 *
167 * @param array<mixed> $payload PHP array
168 * @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
169 * @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
170 * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
171 * @param string $keyId
172 * @param array<string, string|string[]> $head An array with header elements to attach
173 *
174 * @return string A signed JWT
175 *
176 * @uses jsonEncode
177 * @uses urlsafeB64Encode
178 */
179 public static function encode(array $payload, #[\SensitiveParameter] $key, string $alg, ?string $keyId = null, ?array $head = null) : string
180 {
181 $header = ['typ' => 'JWT'];
182 if (isset($head)) {
183 $header = \array_merge($header, $head);
184 }
185 $header['alg'] = $alg;
186 if ($keyId !== null) {
187 $header['kid'] = $keyId;
188 }
189 $segments = [];
190 $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($header));
191 $segments[] = static::urlsafeB64Encode((string) static::jsonEncode($payload));
192 $signing_input = \implode('.', $segments);
193 $signature = static::sign($signing_input, $key, $alg);
194 $segments[] = static::urlsafeB64Encode($signature);
195 return \implode('.', $segments);
196 }
197 /**
198 * Sign a string with a given key and algorithm.
199 *
200 * @param string $msg The message to sign
201 * @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
202 * @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256',
203 * 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
204 *
205 * @return string An encrypted message
206 *
207 * @throws DomainException Unsupported algorithm or bad key was specified
208 */
209 public static function sign(string $msg, #[\SensitiveParameter] $key, string $alg) : string
210 {
211 if (empty(static::$supported_algs[$alg])) {
212 throw new DomainException('Algorithm not supported');
213 }
214 list($function, $algorithm) = static::$supported_algs[$alg];
215 switch ($function) {
216 case 'hash_hmac':
217 if (!\is_string($key)) {
218 throw new InvalidArgumentException('key must be a string when using hmac');
219 }
220 self::validateHmacKeyLength($key, $algorithm);
221 return \hash_hmac($algorithm, $msg, $key, \true);
222 case 'openssl':
223 $signature = '';
224 if (!($key = \openssl_pkey_get_private($key))) {
225 throw new DomainException('OpenSSL unable to validate key');
226 }
227 if (\str_starts_with($alg, 'RS')) {
228 self::validateRsaKeyLength($key);
229 } elseif (\str_starts_with($alg, 'ES')) {
230 self::validateEcKeyLength($key, $alg);
231 }
232 $success = \openssl_sign($msg, $signature, $key, $algorithm);
233 if (!$success) {
234 throw new DomainException('OpenSSL unable to sign data');
235 }
236 if ($alg === 'ES256' || $alg === 'ES256K') {
237 $signature = self::signatureFromDER($signature, 256);
238 } elseif ($alg === 'ES384') {
239 $signature = self::signatureFromDER($signature, 384);
240 }
241 return $signature;
242 case 'sodium_crypto':
243 if (!\function_exists('sodium_crypto_sign_detached')) {
244 throw new DomainException('libsodium is not available');
245 }
246 if (!\is_string($key)) {
247 throw new InvalidArgumentException('key must be a string when using EdDSA');
248 }
249 try {
250 // The last non-empty line is used as the key.
251 $lines = \array_filter(\explode("\n", $key));
252 $key = \base64_decode((string) \end($lines));
253 if (\strlen($key) === 0) {
254 throw new DomainException('Key cannot be empty string');
255 }
256 return \sodium_crypto_sign_detached($msg, $key);
257 } catch (Exception $e) {
258 throw new DomainException($e->getMessage(), 0, $e);
259 }
260 }
261 throw new DomainException('Algorithm not supported');
262 }
263 /**
264 * Verify a signature with the message, key and method. Not all methods
265 * are symmetric, so we must have a separate verify and sign method.
266 *
267 * @param string $msg The original message (header and body)
268 * @param string $signature The original signature
269 * @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
270 * @param string $alg The algorithm
271 *
272 * @return bool
273 *
274 * @throws DomainException Invalid Algorithm, bad key, or OpenSSL failure
275 */
276 private static function verify(string $msg, string $signature, #[\SensitiveParameter] $keyMaterial, string $alg) : bool
277 {
278 if (empty(static::$supported_algs[$alg])) {
279 throw new DomainException('Algorithm not supported');
280 }
281 list($function, $algorithm) = static::$supported_algs[$alg];
282 switch ($function) {
283 case 'openssl':
284 if (!($key = \openssl_pkey_get_public($keyMaterial))) {
285 throw new DomainException('OpenSSL unable to validate key');
286 }
287 if (\str_starts_with($alg, 'RS')) {
288 self::validateRsaKeyLength($key);
289 } elseif (\str_starts_with($alg, 'ES')) {
290 self::validateEcKeyLength($key, $alg);
291 }
292 $success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm);
293 if ($success === 1) {
294 return \true;
295 }
296 if ($success === 0) {
297 return \false;
298 }
299 // returns 1 on success, 0 on failure, -1 on error.
300 throw new DomainException('OpenSSL error: ' . \openssl_error_string());
301 case 'sodium_crypto':
302 if (!\function_exists('sodium_crypto_sign_verify_detached')) {
303 throw new DomainException('libsodium is not available');
304 }
305 if (!\is_string($keyMaterial)) {
306 throw new InvalidArgumentException('key must be a string when using EdDSA');
307 }
308 try {
309 // The last non-empty line is used as the key.
310 $lines = \array_filter(\explode("\n", $keyMaterial));
311 $key = \base64_decode((string) \end($lines));
312 if (\strlen($key) === 0) {
313 throw new DomainException('Key cannot be empty string');
314 }
315 if (\strlen($signature) === 0) {
316 throw new DomainException('Signature cannot be empty string');
317 }
318 return \sodium_crypto_sign_verify_detached($signature, $msg, $key);
319 } catch (Exception $e) {
320 throw new DomainException($e->getMessage(), 0, $e);
321 }
322 case 'hash_hmac':
323 default:
324 if (!\is_string($keyMaterial)) {
325 throw new InvalidArgumentException('key must be a string when using hmac');
326 }
327 self::validateHmacKeyLength($keyMaterial, $algorithm);
328 $hash = \hash_hmac($algorithm, $msg, $keyMaterial, \true);
329 return self::constantTimeEquals($hash, $signature);
330 }
331 }
332 /**
333 * Decode a JSON string into a PHP object.
334 *
335 * @param string $input JSON string
336 *
337 * @return mixed The decoded JSON string
338 *
339 * @throws DomainException Provided string was invalid JSON
340 */
341 public static function jsonDecode(string $input)
342 {
343 $obj = \json_decode($input, \false, 512, \JSON_BIGINT_AS_STRING);
344 if ($errno = \json_last_error()) {
345 self::handleJsonError($errno);
346 } elseif ($obj === null && $input !== 'null') {
347 throw new DomainException('Null result with non-null input');
348 }
349 return $obj;
350 }
351 /**
352 * Encode a PHP array into a JSON string.
353 *
354 * @param array<mixed> $input A PHP array
355 *
356 * @return string JSON representation of the PHP array
357 *
358 * @throws DomainException Provided object could not be encoded to valid JSON
359 */
360 public static function jsonEncode(array $input) : string
361 {
362 $json = \json_encode($input, \JSON_UNESCAPED_SLASHES);
363 if ($errno = \json_last_error()) {
364 self::handleJsonError($errno);
365 } elseif ($json === 'null') {
366 throw new DomainException('Null result with non-null input');
367 }
368 if ($json === \false) {
369 throw new DomainException('Provided object could not be encoded to valid JSON');
370 }
371 return $json;
372 }
373 /**
374 * Decode a string with URL-safe Base64.
375 *
376 * @param string $input A Base64 encoded string
377 *
378 * @return string A decoded string
379 *
380 * @throws InvalidArgumentException invalid base64 characters
381 */
382 public static function urlsafeB64Decode(string $input) : string
383 {
384 return \base64_decode(self::convertBase64UrlToBase64($input));
385 }
386 /**
387 * Convert a string in the base64url (URL-safe Base64) encoding to standard base64.
388 *
389 * @param string $input A Base64 encoded string with URL-safe characters (-_ and no padding)
390 *
391 * @return string A Base64 encoded string with standard characters (+/) and padding (=), when
392 * needed.
393 *
394 * @see https://www.rfc-editor.org/rfc/rfc4648
395 */
396 public static function convertBase64UrlToBase64(string $input) : string
397 {
398 $remainder = \strlen($input) % 4;
399 if ($remainder) {
400 $padlen = 4 - $remainder;
401 $input .= \str_repeat('=', $padlen);
402 }
403 return \strtr($input, '-_', '+/');
404 }
405 /**
406 * Encode a string with URL-safe Base64.
407 *
408 * @param string $input The string you want encoded
409 *
410 * @return string The base64 encode of what you passed in
411 */
412 public static function urlsafeB64Encode(string $input) : string
413 {
414 return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
415 }
416 /**
417 * Determine if an algorithm has been provided for each Key
418 *
419 * @param Key|ArrayAccess<string,Key>|array<string,Key> $keyOrKeyArray
420 * @param string|null $kid
421 *
422 * @throws UnexpectedValueException
423 *
424 * @return Key
425 */
426 private static function getKey(#[\SensitiveParameter] $keyOrKeyArray, ?string $kid) : Key
427 {
428 if ($keyOrKeyArray instanceof Key) {
429 return $keyOrKeyArray;
430 }
431 if (empty($kid) && $kid !== '0') {
432 throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
433 }
434 if ($keyOrKeyArray instanceof CachedKeySet) {
435 // Skip "isset" check, as this will automatically refresh if not set
436 return $keyOrKeyArray[$kid];
437 }
438 if (!isset($keyOrKeyArray[$kid])) {
439 throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
440 }
441 return $keyOrKeyArray[$kid];
442 }
443 /**
444 * @param string $left The string of known length to compare against
445 * @param string $right The user-supplied string
446 * @return bool
447 */
448 public static function constantTimeEquals(string $left, string $right) : bool
449 {
450 if (\function_exists('hash_equals')) {
451 return \hash_equals($left, $right);
452 }
453 $len = \min(self::safeStrlen($left), self::safeStrlen($right));
454 $status = 0;
455 for ($i = 0; $i < $len; $i++) {
456 $status |= \ord($left[$i]) ^ \ord($right[$i]);
457 }
458 $status |= self::safeStrlen($left) ^ self::safeStrlen($right);
459 return $status === 0;
460 }
461 /**
462 * Helper method to create a JSON error.
463 *
464 * @param int $errno An error number from json_last_error()
465 *
466 * @throws DomainException
467 *
468 * @return void
469 */
470 private static function handleJsonError(int $errno) : void
471 {
472 $messages = [\JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', \JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters'];
473 throw new DomainException(isset($messages[$errno]) ? $messages[$errno] : 'Unknown JSON error: ' . $errno);
474 }
475 /**
476 * Get the number of bytes in cryptographic strings.
477 *
478 * @param string $str
479 *
480 * @return int
481 */
482 private static function safeStrlen(string $str) : int
483 {
484 if (\function_exists('mb_strlen')) {
485 return \mb_strlen($str, '8bit');
486 }
487 return \strlen($str);
488 }
489 /**
490 * Convert an ECDSA signature to an ASN.1 DER sequence
491 *
492 * @param string $sig The ECDSA signature to convert
493 * @return string The encoded DER object
494 */
495 private static function signatureToDER(string $sig) : string
496 {
497 // Separate the signature into r-value and s-value
498 $length = \max(1, (int) (\strlen($sig) / 2));
499 list($r, $s) = \str_split($sig, $length);
500 // Trim leading zeros
501 $r = \ltrim($r, "\x00");
502 $s = \ltrim($s, "\x00");
503 // Convert r-value and s-value from unsigned big-endian integers to
504 // signed two's complement
505 if (\ord($r[0]) > 0x7f) {
506 $r = "\x00" . $r;
507 }
508 if (\ord($s[0]) > 0x7f) {
509 $s = "\x00" . $s;
510 }
511 return self::encodeDER(self::ASN1_SEQUENCE, self::encodeDER(self::ASN1_INTEGER, $r) . self::encodeDER(self::ASN1_INTEGER, $s));
512 }
513 /**
514 * Encodes a value into a DER object.
515 *
516 * @param int $type DER tag
517 * @param string $value the value to encode
518 *
519 * @return string the encoded object
520 */
521 private static function encodeDER(int $type, string $value) : string
522 {
523 $tag_header = 0;
524 if ($type === self::ASN1_SEQUENCE) {
525 $tag_header |= 0x20;
526 }
527 // Type
528 $der = \chr($tag_header | $type);
529 // Length
530 $der .= \chr(\strlen($value));
531 return $der . $value;
532 }
533 /**
534 * Encodes signature from a DER object.
535 *
536 * @param string $der binary signature in DER format
537 * @param int $keySize the number of bits in the key
538 *
539 * @return string the signature
540 */
541 private static function signatureFromDER(string $der, int $keySize) : string
542 {
543 // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
544 list($offset, $_) = self::readDER($der);
545 list($offset, $r) = self::readDER($der, $offset);
546 list($offset, $s) = self::readDER($der, $offset);
547 // Convert r-value and s-value from signed two's compliment to unsigned
548 // big-endian integers
549 $r = \ltrim($r, "\x00");
550 $s = \ltrim($s, "\x00");
551 // Pad out r and s so that they are $keySize bits long
552 $r = \str_pad($r, $keySize / 8, "\x00", \STR_PAD_LEFT);
553 $s = \str_pad($s, $keySize / 8, "\x00", \STR_PAD_LEFT);
554 return $r . $s;
555 }
556 /**
557 * Reads binary DER-encoded data and decodes into a single object
558 *
559 * @param string $der the binary data in DER format
560 * @param int $offset the offset of the data stream containing the object
561 * to decode
562 *
563 * @return array{int, string|null} the new offset and the decoded object
564 */
565 private static function readDER(string $der, int $offset = 0) : array
566 {
567 $pos = $offset;
568 $size = \strlen($der);
569 $constructed = \ord($der[$pos]) >> 5 & 0x1;
570 $type = \ord($der[$pos++]) & 0x1f;
571 // Length
572 $len = \ord($der[$pos++]);
573 if ($len & 0x80) {
574 $n = $len & 0x1f;
575 $len = 0;
576 while ($n-- && $pos < $size) {
577 $len = $len << 8 | \ord($der[$pos++]);
578 }
579 }
580 // Value
581 if ($type === self::ASN1_BIT_STRING) {
582 $pos++;
583 // Skip the first contents octet (padding indicator)
584 $data = \substr($der, $pos, $len - 1);
585 $pos += $len - 1;
586 } elseif (!$constructed) {
587 $data = \substr($der, $pos, $len);
588 $pos += $len;
589 } else {
590 $data = null;
591 }
592 return [$pos, $data];
593 }
594 /**
595 * Validate HMAC key length
596 *
597 * @param string $key HMAC key material
598 * @param string $algorithm The algorithm
599 *
600 * @throws DomainException Provided key is too short
601 */
602 private static function validateHmacKeyLength(string $key, string $algorithm) : void
603 {
604 $keyLength = \strlen($key) * 8;
605 $minKeyLength = (int) \str_replace('SHA', '', $algorithm);
606 if ($keyLength < $minKeyLength) {
607 throw new DomainException('Provided key is too short');
608 }
609 }
610 /**
611 * Validate RSA key length
612 *
613 * @param OpenSSLAsymmetricKey $key RSA key material
614 * @throws DomainException Provided key is too short
615 */
616 private static function validateRsaKeyLength(#[\SensitiveParameter] OpenSSLAsymmetricKey $key) : void
617 {
618 if (!($keyDetails = \openssl_pkey_get_details($key))) {
619 throw new DomainException('Unable to validate key');
620 }
621 if ($keyDetails['bits'] < self::RSA_KEY_MIN_LENGTH) {
622 throw new DomainException('Provided key is too short');
623 }
624 }
625 /**
626 * Validate RSA key length
627 *
628 * @param OpenSSLAsymmetricKey $key RSA key material
629 * @param string $algorithm The algorithm
630 * @throws DomainException Provided key is too short
631 */
632 private static function validateEcKeyLength(#[\SensitiveParameter] OpenSSLAsymmetricKey $key, string $algorithm) : void
633 {
634 if (!($keyDetails = \openssl_pkey_get_details($key))) {
635 throw new DomainException('Unable to validate key');
636 }
637 $minKeyLength = (int) \str_replace('ES', '', $algorithm);
638 if ($keyDetails['bits'] < $minKeyLength) {
639 throw new DomainException('Provided key is too short');
640 }
641 }
642 }
643