Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 3 namespace Firebase\JWT; 4 5 use DomainException; 6 use InvalidArgumentException; 7 use UnexpectedValueException; 8 9 /** 10 * JSON Web Key implementation, based on this spec: 11 * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41 12 * 13 * PHP version 5 14 * 15 * @category Authentication 16 * @package Authentication_JWT 17 * @author Bui Sy Nguyen <nguyenbs@gmail.com> 18 * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 19 * @link https://github.com/firebase/php-jwt 20 */ 21 class JWK 22 { 23 /** 24 * Parse a set of JWK keys 25 * 26 * @param array $jwks The JSON Web Key Set as an associative array 27 * 28 * @return array<string, Key> An associative array of key IDs (kid) to Key objects 29 * 30 * @throws InvalidArgumentException Provided JWK Set is empty 31 * @throws UnexpectedValueException Provided JWK Set was invalid 32 * @throws DomainException OpenSSL failure 33 * 34 * @uses parseKey 35 */ 36 public static function parseKeySet(array $jwks) 37 { 38 $keys = array(); 39 40 if (!isset($jwks['keys'])) { 41 throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); 42 } 43 if (empty($jwks['keys'])) { 44 throw new InvalidArgumentException('JWK Set did not contain any keys'); 45 } 46 47 foreach ($jwks['keys'] as $k => $v) { 48 $kid = isset($v['kid']) ? $v['kid'] : $k; 49 if ($key = self::parseKey($v)) { 50 $keys[$kid] = $key; 51 } 52 } 53 54 if (0 === \count($keys)) { 55 throw new UnexpectedValueException('No supported algorithms found in JWK Set'); 56 } 57 58 return $keys; 59 } 60 61 /** 62 * Parse a JWK key 63 * 64 * @param array $jwk An individual JWK 65 * 66 * @return Key The key object for the JWK 67 * 68 * @throws InvalidArgumentException Provided JWK is empty 69 * @throws UnexpectedValueException Provided JWK was invalid 70 * @throws DomainException OpenSSL failure 71 * 72 * @uses createPemFromModulusAndExponent 73 */ 74 public static function parseKey(array $jwk) 75 { 76 if (empty($jwk)) { 77 throw new InvalidArgumentException('JWK must not be empty'); 78 } 79 if (!isset($jwk['kty'])) { 80 throw new UnexpectedValueException('JWK must contain a "kty" parameter'); 81 } 82 if (!isset($jwk['alg'])) { 83 // The "alg" parameter is optional in a KTY, but is required for parsing in 84 // this library. Add it manually to your JWK array if it doesn't already exist. 85 // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4 86 throw new UnexpectedValueException('JWK must contain an "alg" parameter'); 87 } 88 89 switch ($jwk['kty']) { 90 case 'RSA': 91 if (!empty($jwk['d'])) { 92 throw new UnexpectedValueException('RSA private keys are not supported'); 93 } 94 if (!isset($jwk['n']) || !isset($jwk['e'])) { 95 throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"'); 96 } 97 98 $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']); 99 $publicKey = \openssl_pkey_get_public($pem); 100 if (false === $publicKey) { 101 throw new DomainException( 102 'OpenSSL error: ' . \openssl_error_string() 103 ); 104 } 105 return new Key($publicKey, $jwk['alg']); 106 default: 107 // Currently only RSA is supported 108 break; 109 } 110 } 111 112 /** 113 * Create a public key represented in PEM format from RSA modulus and exponent information 114 * 115 * @param string $n The RSA modulus encoded in Base64 116 * @param string $e The RSA exponent encoded in Base64 117 * 118 * @return string The RSA public key represented in PEM format 119 * 120 * @uses encodeLength 121 */ 122 private static function createPemFromModulusAndExponent($n, $e) 123 { 124 $modulus = JWT::urlsafeB64Decode($n); 125 $publicExponent = JWT::urlsafeB64Decode($e); 126 127 $components = array( 128 'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus), 129 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent) 130 ); 131 132 $rsaPublicKey = \pack( 133 'Ca*a*a*', 134 48, 135 self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])), 136 $components['modulus'], 137 $components['publicExponent'] 138 ); 139 140 // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. 141 $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA 142 $rsaPublicKey = \chr(0) . $rsaPublicKey; 143 $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey; 144 145 $rsaPublicKey = \pack( 146 'Ca*a*', 147 48, 148 self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), 149 $rsaOID . $rsaPublicKey 150 ); 151 152 $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . 153 \chunk_split(\base64_encode($rsaPublicKey), 64) . 154 '-----END PUBLIC KEY-----'; 155 156 return $rsaPublicKey; 157 } 158 159 /** 160 * DER-encode the length 161 * 162 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See 163 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. 164 * 165 * @param int $length 166 * @return string 167 */ 168 private static function encodeLength($length) 169 { 170 if ($length <= 0x7F) { 171 return \chr($length); 172 } 173 174 $temp = \ltrim(\pack('N', $length), \chr(0)); 175 176 return \pack('Ca*', 0x80 | \strlen($temp), $temp); 177 } 178 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body