weekly release 3.11.8+
[moodle.git] / lib / php-jwt / src / JWK.php
blob1d273917403bb0a509d821401b49a2c35f8747e0
1 <?php
3 namespace Firebase\JWT;
5 use DomainException;
6 use UnexpectedValueException;
8 /**
9 * JSON Web Key implementation, based on this spec:
10 * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
12 * PHP version 5
14 * @category Authentication
15 * @package Authentication_JWT
16 * @author Bui Sy Nguyen <nguyenbs@gmail.com>
17 * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
18 * @link https://github.com/firebase/php-jwt
20 class JWK
22 /**
23 * Parse a set of JWK keys
25 * @param array $jwks The JSON Web Key Set as an associative array
27 * @return array An associative array that represents the set of keys
29 * @throws InvalidArgumentException Provided JWK Set is empty
30 * @throws UnexpectedValueException Provided JWK Set was invalid
31 * @throws DomainException OpenSSL failure
33 * @uses parseKey
35 public static function parseKeySet(array $jwks)
37 $keys = array();
39 if (!isset($jwks['keys'])) {
40 throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
42 if (empty($jwks['keys'])) {
43 throw new InvalidArgumentException('JWK Set did not contain any keys');
46 foreach ($jwks['keys'] as $k => $v) {
47 $kid = isset($v['kid']) ? $v['kid'] : $k;
48 if ($key = self::parseKey($v)) {
49 $keys[$kid] = $key;
53 if (0 === \count($keys)) {
54 throw new UnexpectedValueException('No supported algorithms found in JWK Set');
57 return $keys;
60 /**
61 * Parse a JWK key
63 * @param array $jwk An individual JWK
65 * @return resource|array An associative array that represents the key
67 * @throws InvalidArgumentException Provided JWK is empty
68 * @throws UnexpectedValueException Provided JWK was invalid
69 * @throws DomainException OpenSSL failure
71 * @uses createPemFromModulusAndExponent
73 private static function parseKey(array $jwk)
75 if (empty($jwk)) {
76 throw new InvalidArgumentException('JWK must not be empty');
78 if (!isset($jwk['kty'])) {
79 throw new UnexpectedValueException('JWK must contain a "kty" parameter');
82 switch ($jwk['kty']) {
83 case 'RSA':
84 if (\array_key_exists('d', $jwk)) {
85 throw new UnexpectedValueException('RSA private keys are not supported');
87 if (!isset($jwk['n']) || !isset($jwk['e'])) {
88 throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
91 $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
92 $publicKey = \openssl_pkey_get_public($pem);
93 if (false === $publicKey) {
94 throw new DomainException(
95 'OpenSSL error: ' . \openssl_error_string()
98 return $publicKey;
99 default:
100 // Currently only RSA is supported
101 break;
106 * Create a public key represented in PEM format from RSA modulus and exponent information
108 * @param string $n The RSA modulus encoded in Base64
109 * @param string $e The RSA exponent encoded in Base64
111 * @return string The RSA public key represented in PEM format
113 * @uses encodeLength
115 private static function createPemFromModulusAndExponent($n, $e)
117 $modulus = JWT::urlsafeB64Decode($n);
118 $publicExponent = JWT::urlsafeB64Decode($e);
120 $components = array(
121 'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
122 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
125 $rsaPublicKey = \pack(
126 'Ca*a*a*',
128 self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
129 $components['modulus'],
130 $components['publicExponent']
133 // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
134 $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
135 $rsaPublicKey = \chr(0) . $rsaPublicKey;
136 $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
138 $rsaPublicKey = \pack(
139 'Ca*a*',
141 self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
142 $rsaOID . $rsaPublicKey
145 $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
146 \chunk_split(\base64_encode($rsaPublicKey), 64) .
147 '-----END PUBLIC KEY-----';
149 return $rsaPublicKey;
153 * DER-encode the length
155 * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
156 * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
158 * @param int $length
159 * @return string
161 private static function encodeLength($length)
163 if ($length <= 0x7F) {
164 return \chr($length);
167 $temp = \ltrim(\pack('N', $length), \chr(0));
169 return \pack('Ca*', 0x80 | \strlen($temp), $temp);