Browse code

added appinfo/info.xml appinfo/signature.json CHANGELOG.txt lib/AppInfo/Application.php css/style.css providers/Plivo

DoubleBastionAdmin authored on 05/11/2025 13:35:09
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,355 @@
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
+    private const OID = '1.2.840.10045.2.1';
24
+    private const ASN1_OBJECT_IDENTIFIER = 0x06;
25
+    private const ASN1_SEQUENCE = 0x10; // also defined in JWT
26
+    private const ASN1_BIT_STRING = 0x03;
27
+    private const EC_CURVES = [
28
+        'P-256' => '1.2.840.10045.3.1.7', // Len: 64
29
+        'secp256k1' => '1.3.132.0.10', // Len: 64
30
+        'P-384' => '1.3.132.0.34', // Len: 96
31
+        // 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
32
+    ];
33
+
34
+    // For keys with "kty" equal to "OKP" (Octet Key Pair), the "crv" parameter must contain the key subtype.
35
+    // This library supports the following subtypes:
36
+    private const OKP_SUBTYPES = [
37
+        'Ed25519' => true, // RFC 8037
38
+    ];
39
+
40
+    /**
41
+     * Parse a set of JWK keys
42
+     *
43
+     * @param array<mixed> $jwks The JSON Web Key Set as an associative array
44
+     * @param string       $defaultAlg The algorithm for the Key object if "alg" is not set in the
45
+     *                                 JSON Web Key Set
46
+     *
47
+     * @return array<string, Key> An associative array of key IDs (kid) to Key objects
48
+     *
49
+     * @throws InvalidArgumentException     Provided JWK Set is empty
50
+     * @throws UnexpectedValueException     Provided JWK Set was invalid
51
+     * @throws DomainException              OpenSSL failure
52
+     *
53
+     * @uses parseKey
54
+     */
55
+    public static function parseKeySet(array $jwks, ?string $defaultAlg = null): array
56
+    {
57
+        $keys = [];
58
+
59
+        if (!isset($jwks['keys'])) {
60
+            throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
61
+        }
62
+
63
+        if (empty($jwks['keys'])) {
64
+            throw new InvalidArgumentException('JWK Set did not contain any keys');
65
+        }
66
+
67
+        foreach ($jwks['keys'] as $k => $v) {
68
+            $kid = isset($v['kid']) ? $v['kid'] : $k;
69
+            if ($key = self::parseKey($v, $defaultAlg)) {
70
+                $keys[(string) $kid] = $key;
71
+            }
72
+        }
73
+
74
+        if (0 === \count($keys)) {
75
+            throw new UnexpectedValueException('No supported algorithms found in JWK Set');
76
+        }
77
+
78
+        return $keys;
79
+    }
80
+
81
+    /**
82
+     * Parse a JWK key
83
+     *
84
+     * @param array<mixed> $jwk An individual JWK
85
+     * @param string       $defaultAlg The algorithm for the Key object if "alg" is not set in the
86
+     *                                 JSON Web Key Set
87
+     *
88
+     * @return Key The key object for the JWK
89
+     *
90
+     * @throws InvalidArgumentException     Provided JWK is empty
91
+     * @throws UnexpectedValueException     Provided JWK was invalid
92
+     * @throws DomainException              OpenSSL failure
93
+     *
94
+     * @uses createPemFromModulusAndExponent
95
+     */
96
+    public static function parseKey(array $jwk, ?string $defaultAlg = null): ?Key
97
+    {
98
+        if (empty($jwk)) {
99
+            throw new InvalidArgumentException('JWK must not be empty');
100
+        }
101
+
102
+        if (!isset($jwk['kty'])) {
103
+            throw new UnexpectedValueException('JWK must contain a "kty" parameter');
104
+        }
105
+
106
+        if (!isset($jwk['alg'])) {
107
+            if (\is_null($defaultAlg)) {
108
+                // The "alg" parameter is optional in a KTY, but an algorithm is required
109
+                // for parsing in this library. Use the $defaultAlg parameter when parsing the
110
+                // key set in order to prevent this error.
111
+                // @see https://datatracker.ietf.org/doc/html/rfc7517#section-4.4
112
+                throw new UnexpectedValueException('JWK must contain an "alg" parameter');
113
+            }
114
+            $jwk['alg'] = $defaultAlg;
115
+        }
116
+
117
+        switch ($jwk['kty']) {
118
+            case 'RSA':
119
+                if (!empty($jwk['d'])) {
120
+                    throw new UnexpectedValueException('RSA private keys are not supported');
121
+                }
122
+                if (!isset($jwk['n']) || !isset($jwk['e'])) {
123
+                    throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
124
+                }
125
+
126
+                $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
127
+                $publicKey = \openssl_pkey_get_public($pem);
128
+                if (false === $publicKey) {
129
+                    throw new DomainException(
130
+                        'OpenSSL error: ' . \openssl_error_string()
131
+                    );
132
+                }
133
+                return new Key($publicKey, $jwk['alg']);
134
+            case 'EC':
135
+                if (isset($jwk['d'])) {
136
+                    // The key is actually a private key
137
+                    throw new UnexpectedValueException('Key data must be for a public key');
138
+                }
139
+
140
+                if (empty($jwk['crv'])) {
141
+                    throw new UnexpectedValueException('crv not set');
142
+                }
143
+
144
+                if (!isset(self::EC_CURVES[$jwk['crv']])) {
145
+                    throw new DomainException('Unrecognised or unsupported EC curve');
146
+                }
147
+
148
+                if (empty($jwk['x']) || empty($jwk['y'])) {
149
+                    throw new UnexpectedValueException('x and y not set');
150
+                }
151
+
152
+                $publicKey = self::createPemFromCrvAndXYCoordinates($jwk['crv'], $jwk['x'], $jwk['y']);
153
+                return new Key($publicKey, $jwk['alg']);
154
+            case 'OKP':
155
+                if (isset($jwk['d'])) {
156
+                    // The key is actually a private key
157
+                    throw new UnexpectedValueException('Key data must be for a public key');
158
+                }
159
+
160
+                if (!isset($jwk['crv'])) {
161
+                    throw new UnexpectedValueException('crv not set');
162
+                }
163
+
164
+                if (empty(self::OKP_SUBTYPES[$jwk['crv']])) {
165
+                    throw new DomainException('Unrecognised or unsupported OKP key subtype');
166
+                }
167
+
168
+                if (empty($jwk['x'])) {
169
+                    throw new UnexpectedValueException('x not set');
170
+                }
171
+
172
+                // This library works internally with EdDSA keys (Ed25519) encoded in standard base64.
173
+                $publicKey = JWT::convertBase64urlToBase64($jwk['x']);
174
+                return new Key($publicKey, $jwk['alg']);
175
+            case 'oct':
176
+                if (!isset($jwk['k'])) {
177
+                    throw new UnexpectedValueException('k not set');
178
+                }
179
+
180
+                return new Key(JWT::urlsafeB64Decode($jwk['k']), $jwk['alg']);
181
+            default:
182
+                break;
183
+        }
184
+
185
+        return null;
186
+    }
187
+
188
+    /**
189
+     * Converts the EC JWK values to pem format.
190
+     *
191
+     * @param   string  $crv The EC curve (only P-256 & P-384 is supported)
192
+     * @param   string  $x   The EC x-coordinate
193
+     * @param   string  $y   The EC y-coordinate
194
+     *
195
+     * @return  string
196
+     */
197
+    private static function createPemFromCrvAndXYCoordinates(string $crv, string $x, string $y): string
198
+    {
199
+        $pem =
200
+            self::encodeDER(
201
+                self::ASN1_SEQUENCE,
202
+                self::encodeDER(
203
+                    self::ASN1_SEQUENCE,
204
+                    self::encodeDER(
205
+                        self::ASN1_OBJECT_IDENTIFIER,
206
+                        self::encodeOID(self::OID)
207
+                    )
208
+                    . self::encodeDER(
209
+                        self::ASN1_OBJECT_IDENTIFIER,
210
+                        self::encodeOID(self::EC_CURVES[$crv])
211
+                    )
212
+                ) .
213
+                self::encodeDER(
214
+                    self::ASN1_BIT_STRING,
215
+                    \chr(0x00) . \chr(0x04)
216
+                    . JWT::urlsafeB64Decode($x)
217
+                    . JWT::urlsafeB64Decode($y)
218
+                )
219
+            );
220
+
221
+        return \sprintf(
222
+            "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
223
+            wordwrap(base64_encode($pem), 64, "\n", true)
224
+        );
225
+    }
226
+
227
+    /**
228
+     * Create a public key represented in PEM format from RSA modulus and exponent information
229
+     *
230
+     * @param string $n The RSA modulus encoded in Base64
231
+     * @param string $e The RSA exponent encoded in Base64
232
+     *
233
+     * @return string The RSA public key represented in PEM format
234
+     *
235
+     * @uses encodeLength
236
+     */
237
+    private static function createPemFromModulusAndExponent(
238
+        string $n,
239
+        string $e
240
+    ): string {
241
+        $mod = JWT::urlsafeB64Decode($n);
242
+        $exp = JWT::urlsafeB64Decode($e);
243
+
244
+        $modulus = \pack('Ca*a*', 2, self::encodeLength(\strlen($mod)), $mod);
245
+        $publicExponent = \pack('Ca*a*', 2, self::encodeLength(\strlen($exp)), $exp);
246
+
247
+        $rsaPublicKey = \pack(
248
+            'Ca*a*a*',
249
+            48,
250
+            self::encodeLength(\strlen($modulus) + \strlen($publicExponent)),
251
+            $modulus,
252
+            $publicExponent
253
+        );
254
+
255
+        // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
256
+        $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
257
+        $rsaPublicKey = \chr(0) . $rsaPublicKey;
258
+        $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
259
+
260
+        $rsaPublicKey = \pack(
261
+            'Ca*a*',
262
+            48,
263
+            self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
264
+            $rsaOID . $rsaPublicKey
265
+        );
266
+
267
+        return "-----BEGIN PUBLIC KEY-----\r\n" .
268
+            \chunk_split(\base64_encode($rsaPublicKey), 64) .
269
+            '-----END PUBLIC KEY-----';
270
+    }
271
+
272
+    /**
273
+     * DER-encode the length
274
+     *
275
+     * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4.  See
276
+     * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
277
+     *
278
+     * @param int $length
279
+     * @return string
280
+     */
281
+    private static function encodeLength(int $length): string
282
+    {
283
+        if ($length <= 0x7F) {
284
+            return \chr($length);
285
+        }
286
+
287
+        $temp = \ltrim(\pack('N', $length), \chr(0));
288
+
289
+        return \pack('Ca*', 0x80 | \strlen($temp), $temp);
290
+    }
291
+
292
+    /**
293
+     * Encodes a value into a DER object.
294
+     * Also defined in Firebase\JWT\JWT
295
+     *
296
+     * @param   int     $type DER tag
297
+     * @param   string  $value the value to encode
298
+     * @return  string  the encoded object
299
+     */
300
+    private static function encodeDER(int $type, string $value): string
301
+    {
302
+        $tag_header = 0;
303
+        if ($type === self::ASN1_SEQUENCE) {
304
+            $tag_header |= 0x20;
305
+        }
306
+
307
+        // Type
308
+        $der = \chr($tag_header | $type);
309
+
310
+        // Length
311
+        $der .= \chr(\strlen($value));
312
+
313
+        return $der . $value;
314
+    }
315
+
316
+    /**
317
+     * Encodes a string into a DER-encoded OID.
318
+     *
319
+     * @param   string $oid the OID string
320
+     * @return  string the binary DER-encoded OID
321
+     */
322
+    private static function encodeOID(string $oid): string
323
+    {
324
+        $octets = explode('.', $oid);
325
+
326
+        // Get the first octet
327
+        $first = (int) array_shift($octets);
328
+        $second = (int) array_shift($octets);
329
+        $oid = \chr($first * 40 + $second);
330
+
331
+        // Iterate over subsequent octets
332
+        foreach ($octets as $octet) {
333
+            if ($octet == 0) {
334
+                $oid .= \chr(0x00);
335
+                continue;
336
+            }
337
+            $bin = '';
338
+
339
+            while ($octet) {
340
+                $bin .= \chr(0x80 | ($octet & 0x7f));
341
+                $octet >>= 7;
342
+            }
343
+            $bin[0] = $bin[0] & \chr(0x7f);
344
+
345
+            // Convert to big endian if necessary
346
+            if (pack('V', 65534) == pack('L', 65534)) {
347
+                $oid .= strrev($bin);
348
+            } else {
349
+                $oid .= $bin;
350
+            }
351
+        }
352
+
353
+        return $oid;
354
+    }
355
+}
Browse code

removed appinfo/info.xml appinfo/signature.json CHANGELOG.txt lib/AppInfo/Application.php css/style.css providers/Plivo

DoubleBastionAdmin authored on 05/11/2025 13:12:22
Showing 1 changed files
1 1
deleted file mode 100644
... ...
@@ -1,171 +0,0 @@
1
-<?php
2
-
3
-namespace Firebase\JWT;
4
-
5
-use DomainException;
6
-use UnexpectedValueException;
7
-
8
-/**
9
- * JSON Web Key implementation, based on this spec:
10
- * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
11
- *
12
- * PHP version 5
13
- *
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
19
- */
20
-class JWK
21
-{
22
-    /**
23
-     * Parse a set of JWK keys
24
-     *
25
-     * @param array $jwks The JSON Web Key Set as an associative array
26
-     *
27
-     * @return array An associative array that represents the set of keys
28
-     *
29
-     * @throws InvalidArgumentException     Provided JWK Set is empty
30
-     * @throws UnexpectedValueException     Provided JWK Set was invalid
31
-     * @throws DomainException              OpenSSL failure
32
-     *
33
-     * @uses parseKey
34
-     */
35
-    public static function parseKeySet(array $jwks)
36
-    {
37
-        $keys = array();
38
-
39
-        if (!isset($jwks['keys'])) {
40
-            throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
41
-        }
42
-        if (empty($jwks['keys'])) {
43
-            throw new InvalidArgumentException('JWK Set did not contain any keys');
44
-        }
45
-
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;
50
-            }
51
-        }
52
-
53
-        if (0 === \count($keys)) {
54
-            throw new UnexpectedValueException('No supported algorithms found in JWK Set');
55
-        }
56
-
57
-        return $keys;
58
-    }
59
-
60
-    /**
61
-     * Parse a JWK key
62
-     *
63
-     * @param array $jwk An individual JWK
64
-     *
65
-     * @return resource|array An associative array that represents the key
66
-     *
67
-     * @throws InvalidArgumentException     Provided JWK is empty
68
-     * @throws UnexpectedValueException     Provided JWK was invalid
69
-     * @throws DomainException              OpenSSL failure
70
-     *
71
-     * @uses createPemFromModulusAndExponent
72
-     */
73
-    private static function parseKey(array $jwk)
74
-    {
75
-        if (empty($jwk)) {
76
-            throw new InvalidArgumentException('JWK must not be empty');
77
-        }
78
-        if (!isset($jwk['kty'])) {
79
-            throw new UnexpectedValueException('JWK must contain a "kty" parameter');
80
-        }
81
-
82
-        switch ($jwk['kty']) {
83
-            case 'RSA':
84
-                if (\array_key_exists('d', $jwk)) {
85
-                    throw new UnexpectedValueException('RSA private keys are not supported');
86
-                }
87
-                if (!isset($jwk['n']) || !isset($jwk['e'])) {
88
-                    throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
89
-                }
90
-
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()
96
-                    );
97
-                }
98
-                return $publicKey;
99
-            default:
100
-                // Currently only RSA is supported
101
-                break;
102
-        }
103
-    }
104
-
105
-    /**
106
-     * Create a public key represented in PEM format from RSA modulus and exponent information
107
-     *
108
-     * @param string $n The RSA modulus encoded in Base64
109
-     * @param string $e The RSA exponent encoded in Base64
110
-     *
111
-     * @return string The RSA public key represented in PEM format
112
-     *
113
-     * @uses encodeLength
114
-     */
115
-    private static function createPemFromModulusAndExponent($n, $e)
116
-    {
117
-        $modulus = JWT::urlsafeB64Decode($n);
118
-        $publicExponent = JWT::urlsafeB64Decode($e);
119
-
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)
123
-        );
124
-
125
-        $rsaPublicKey = \pack(
126
-            'Ca*a*a*',
127
-            48,
128
-            self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
129
-            $components['modulus'],
130
-            $components['publicExponent']
131
-        );
132
-
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;
137
-
138
-        $rsaPublicKey = \pack(
139
-            'Ca*a*',
140
-            48,
141
-            self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
142
-            $rsaOID . $rsaPublicKey
143
-        );
144
-
145
-        $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
146
-            \chunk_split(\base64_encode($rsaPublicKey), 64) .
147
-            '-----END PUBLIC KEY-----';
148
-
149
-        return $rsaPublicKey;
150
-    }
151
-
152
-    /**
153
-     * DER-encode the length
154
-     *
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.
157
-     *
158
-     * @param int $length
159
-     * @return string
160
-     */
161
-    private static function encodeLength($length)
162
-    {
163
-        if ($length <= 0x7F) {
164
-            return \chr($length);
165
-        }
166
-
167
-        $temp = \ltrim(\pack('N', $length), \chr(0));
168
-
169
-        return \pack('Ca*', 0x80 | \strlen($temp), $temp);
170
-    }
171
-}
Browse code

Added README.md appinfo/info.xml appinfo/signature.json lib/Controller/AuthorApiController.php and the providers directory

DoubleBastionAdmin authored on 20/08/2022 16:33:00
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,171 @@
1
+<?php
2
+
3
+namespace Firebase\JWT;
4
+
5
+use DomainException;
6
+use UnexpectedValueException;
7
+
8
+/**
9
+ * JSON Web Key implementation, based on this spec:
10
+ * https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
11
+ *
12
+ * PHP version 5
13
+ *
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
19
+ */
20
+class JWK
21
+{
22
+    /**
23
+     * Parse a set of JWK keys
24
+     *
25
+     * @param array $jwks The JSON Web Key Set as an associative array
26
+     *
27
+     * @return array An associative array that represents the set of keys
28
+     *
29
+     * @throws InvalidArgumentException     Provided JWK Set is empty
30
+     * @throws UnexpectedValueException     Provided JWK Set was invalid
31
+     * @throws DomainException              OpenSSL failure
32
+     *
33
+     * @uses parseKey
34
+     */
35
+    public static function parseKeySet(array $jwks)
36
+    {
37
+        $keys = array();
38
+
39
+        if (!isset($jwks['keys'])) {
40
+            throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
41
+        }
42
+        if (empty($jwks['keys'])) {
43
+            throw new InvalidArgumentException('JWK Set did not contain any keys');
44
+        }
45
+
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;
50
+            }
51
+        }
52
+
53
+        if (0 === \count($keys)) {
54
+            throw new UnexpectedValueException('No supported algorithms found in JWK Set');
55
+        }
56
+
57
+        return $keys;
58
+    }
59
+
60
+    /**
61
+     * Parse a JWK key
62
+     *
63
+     * @param array $jwk An individual JWK
64
+     *
65
+     * @return resource|array An associative array that represents the key
66
+     *
67
+     * @throws InvalidArgumentException     Provided JWK is empty
68
+     * @throws UnexpectedValueException     Provided JWK was invalid
69
+     * @throws DomainException              OpenSSL failure
70
+     *
71
+     * @uses createPemFromModulusAndExponent
72
+     */
73
+    private static function parseKey(array $jwk)
74
+    {
75
+        if (empty($jwk)) {
76
+            throw new InvalidArgumentException('JWK must not be empty');
77
+        }
78
+        if (!isset($jwk['kty'])) {
79
+            throw new UnexpectedValueException('JWK must contain a "kty" parameter');
80
+        }
81
+
82
+        switch ($jwk['kty']) {
83
+            case 'RSA':
84
+                if (\array_key_exists('d', $jwk)) {
85
+                    throw new UnexpectedValueException('RSA private keys are not supported');
86
+                }
87
+                if (!isset($jwk['n']) || !isset($jwk['e'])) {
88
+                    throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
89
+                }
90
+
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()
96
+                    );
97
+                }
98
+                return $publicKey;
99
+            default:
100
+                // Currently only RSA is supported
101
+                break;
102
+        }
103
+    }
104
+
105
+    /**
106
+     * Create a public key represented in PEM format from RSA modulus and exponent information
107
+     *
108
+     * @param string $n The RSA modulus encoded in Base64
109
+     * @param string $e The RSA exponent encoded in Base64
110
+     *
111
+     * @return string The RSA public key represented in PEM format
112
+     *
113
+     * @uses encodeLength
114
+     */
115
+    private static function createPemFromModulusAndExponent($n, $e)
116
+    {
117
+        $modulus = JWT::urlsafeB64Decode($n);
118
+        $publicExponent = JWT::urlsafeB64Decode($e);
119
+
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)
123
+        );
124
+
125
+        $rsaPublicKey = \pack(
126
+            'Ca*a*a*',
127
+            48,
128
+            self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
129
+            $components['modulus'],
130
+            $components['publicExponent']
131
+        );
132
+
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;
137
+
138
+        $rsaPublicKey = \pack(
139
+            'Ca*a*',
140
+            48,
141
+            self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
142
+            $rsaOID . $rsaPublicKey
143
+        );
144
+
145
+        $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
146
+            \chunk_split(\base64_encode($rsaPublicKey), 64) .
147
+            '-----END PUBLIC KEY-----';
148
+
149
+        return $rsaPublicKey;
150
+    }
151
+
152
+    /**
153
+     * DER-encode the length
154
+     *
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.
157
+     *
158
+     * @param int $length
159
+     * @return string
160
+     */
161
+    private static function encodeLength($length)
162
+    {
163
+        if ($length <= 0x7F) {
164
+            return \chr($length);
165
+        }
166
+
167
+        $temp = \ltrim(\pack('N', $length), \chr(0));
168
+
169
+        return \pack('Ca*', 0x80 | \strlen($temp), $temp);
170
+    }
171
+}