1: | <?php
|
2: |
|
3: | namespace Firebase\JWT;
|
4: |
|
5: | use DomainException;
|
6: | use InvalidArgumentException;
|
7: | use UnexpectedValueException;
|
8: |
|
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: |
|
21: | class JWK
|
22: | {
|
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | |
34: | |
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: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | |
71: | |
72: | |
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: |
|
84: |
|
85: |
|
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: |
|
108: | break;
|
109: | }
|
110: | }
|
111: |
|
112: | |
113: | |
114: | |
115: | |
116: | |
117: | |
118: | |
119: | |
120: | |
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: |
|
141: | $rsaOID = \pack('H*', '300d06092a864886f70d0101010500');
|
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: | |
161: | |
162: | |
163: | |
164: | |
165: | |
166: | |
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: | }
|
179: | |