20
20
*/
21
21
class JWK
22
22
{
23
- private static $ oid = '1.2.840.10045.2.1 ' ;
24
- private static $ asn1ObjectIdentifier = 0x06 ;
25
- private static $ asn1Integer = 0x02 ; // also defined in JWT
26
- private static $ asn1Sequence = 0x10 ; // also defined in JWT
27
- private static $ asn1BitString = 0x03 ;
28
- private static $ curves = [
23
+ private const OID = '1.2.840.10045.2.1 ' ;
24
+ private const ASN1_OBJECT_IDENTIFIER = 0x06 ;
25
+ private const ASN1_INTEGER = 0x02 ; // also defined in JWT
26
+ private const ASN1_SEQUENCE = 0x10 ; // also defined in JWT
27
+ private const ASN1_BIT_STRING = 0x03 ;
28
+ private const EC_CURVES = [
29
29
'P-256 ' => '1.2.840.10045.3.1.7 ' , // Len: 64
30
30
// 'P-384' => '1.3.132.0.34', // Len: 96 (not yet supported)
31
31
// 'P-521' => '1.3.132.0.35', // Len: 132 (not supported)
@@ -127,16 +127,15 @@ public static function parseKey(array $jwk): ?Key
127
127
throw new UnexpectedValueException ('crv not set ' );
128
128
}
129
129
130
- if (!isset (self ::$ curves [$ jwk ['crv ' ]])) {
130
+ if (!isset (self ::EC_CURVES [$ jwk ['crv ' ]])) {
131
131
throw new DomainException ('Unrecognised or unsupported EC curve ' );
132
132
}
133
133
134
134
if (empty ($ jwk ['x ' ]) || empty ($ jwk ['y ' ])) {
135
135
throw new UnexpectedValueException ('x and y not set ' );
136
136
}
137
137
138
- $ oid = self ::$ curves [$ jwk ['crv ' ]];
139
- $ publicKey = self ::ecJwkToPem ($ oid , $ jwk ['x ' ], $ jwk ['y ' ]);
138
+ $ publicKey = self ::createPemFromCrvAndXYCoordinates ($ jwk ['crv ' ], $ jwk ['x ' ], $ jwk ['y ' ]);
140
139
return new Key ($ publicKey , $ jwk ['alg ' ]);
141
140
default :
142
141
// Currently only RSA is supported
@@ -146,69 +145,33 @@ public static function parseKey(array $jwk): ?Key
146
145
return null ;
147
146
}
148
147
149
- /**
150
- * Encodes a string into a DER-encoded OID.
151
- *
152
- * @param string $oid the OID string
153
- * @return string the binary DER-encoded OID
154
- */
155
- private static function encodeOID (string $ oid ): string
156
- {
157
- $ octets = explode ('. ' , $ oid );
158
-
159
- // Get the first octet
160
- $ oid = chr (array_shift ($ octets ) * 40 + array_shift ($ octets ));
161
-
162
- // Iterate over subsequent octets
163
- foreach ($ octets as $ octet ) {
164
- if ($ octet == 0 ) {
165
- $ oid .= chr (0x00 );
166
- continue ;
167
- }
168
- $ bin = '' ;
169
-
170
- while ($ octet ) {
171
- $ bin .= chr (0x80 | ($ octet & 0x7f ));
172
- $ octet >>= 7 ;
173
- }
174
- $ bin [0 ] = $ bin [0 ] & chr (0x7f );
175
-
176
- // Convert to big endian if necessary
177
- if (pack ('V ' , 65534 ) == pack ('L ' , 65534 )) {
178
- $ oid .= strrev ($ bin );
179
- } else {
180
- $ oid .= $ bin ;
181
- }
182
- }
183
-
184
- return $ oid ;
185
- }
186
-
187
148
/**
188
149
* Converts the EC JWK values to pem format.
189
150
*
190
- * @param string $oid the OID string
191
- * @param string $x
192
- * @return string $y
151
+ * @param string $crv The EC curve (only P-256 is supported)
152
+ * @param string $x The EC x-coordinate
153
+ * @param string $y The EC y-coordinate
154
+ *
155
+ * @return string
193
156
*/
194
- private static function ecJwkToPem ( $ oid , $ x , $ y )
157
+ private static function createPemFromCrvAndXYCoordinates ( string $ crv , string $ x , string $ y ): string
195
158
{
196
159
$ pem =
197
160
self ::encodeDER (
198
- self ::$ asn1Sequence ,
161
+ self ::ASN1_SEQUENCE ,
199
162
self ::encodeDER (
200
- self ::$ asn1Sequence ,
163
+ self ::ASN1_SEQUENCE ,
201
164
self ::encodeDER (
202
- self ::$ asn1ObjectIdentifier ,
203
- self ::encodeOID (self ::$ oid )
165
+ self ::ASN1_OBJECT_IDENTIFIER ,
166
+ self ::encodeOID (self ::OID )
204
167
)
205
168
. self ::encodeDER (
206
- self ::$ asn1ObjectIdentifier ,
207
- self ::encodeOID ($ oid )
169
+ self ::ASN1_OBJECT_IDENTIFIER ,
170
+ self ::encodeOID (self :: EC_CURVES [ $ crv ] )
208
171
)
209
172
) .
210
173
self ::encodeDER (
211
- self ::$ asn1BitString ,
174
+ self ::ASN1_BIT_STRING ,
212
175
chr (0x00 ) . chr (0x04 )
213
176
. JWT ::urlsafeB64Decode ($ x )
214
177
. JWT ::urlsafeB64Decode ($ y )
@@ -221,30 +184,6 @@ private static function ecJwkToPem($oid, $x, $y)
221
184
);
222
185
}
223
186
224
- /**
225
- * Encodes a value into a DER object.
226
- * Also defined in Firebase\JWT\JWT
227
- *
228
- * @param int $type DER tag
229
- * @param string $value the value to encode
230
- * @return string the encoded object
231
- */
232
- private static function encodeDER (int $ type , string $ value ): string
233
- {
234
- $ tag_header = 0 ;
235
- if ($ type === self ::$ asn1Sequence ) {
236
- $ tag_header |= 0x20 ;
237
- }
238
-
239
- // Type
240
- $ der = \chr ($ tag_header | $ type );
241
-
242
- // Length
243
- $ der .= \chr (\strlen ($ value ));
244
-
245
- return $ der . $ value ;
246
- }
247
-
248
187
/**
249
188
* Create a public key represented in PEM format from RSA modulus and exponent information
250
189
*
@@ -311,4 +250,66 @@ private static function encodeLength(int $length): string
311
250
312
251
return \pack ('Ca* ' , 0x80 | \strlen ($ temp ), $ temp );
313
252
}
253
+
254
+ /**
255
+ * Encodes a value into a DER object.
256
+ * Also defined in Firebase\JWT\JWT
257
+ *
258
+ * @param int $type DER tag
259
+ * @param string $value the value to encode
260
+ * @return string the encoded object
261
+ */
262
+ private static function encodeDER (int $ type , string $ value ): string
263
+ {
264
+ $ tag_header = 0 ;
265
+ if ($ type === self ::ASN1_SEQUENCE ) {
266
+ $ tag_header |= 0x20 ;
267
+ }
268
+
269
+ // Type
270
+ $ der = \chr ($ tag_header | $ type );
271
+
272
+ // Length
273
+ $ der .= \chr (\strlen ($ value ));
274
+
275
+ return $ der . $ value ;
276
+ }
277
+
278
+ /**
279
+ * Encodes a string into a DER-encoded OID.
280
+ *
281
+ * @param string $oid the OID string
282
+ * @return string the binary DER-encoded OID
283
+ */
284
+ private static function encodeOID (string $ oid ): string
285
+ {
286
+ $ octets = explode ('. ' , $ oid );
287
+
288
+ // Get the first octet
289
+ $ oid = chr (array_shift ($ octets ) * 40 + array_shift ($ octets ));
290
+
291
+ // Iterate over subsequent octets
292
+ foreach ($ octets as $ octet ) {
293
+ if ($ octet == 0 ) {
294
+ $ oid .= chr (0x00 );
295
+ continue ;
296
+ }
297
+ $ bin = '' ;
298
+
299
+ while ($ octet ) {
300
+ $ bin .= chr (0x80 | ($ octet & 0x7f ));
301
+ $ octet >>= 7 ;
302
+ }
303
+ $ bin [0 ] = $ bin [0 ] & chr (0x7f );
304
+
305
+ // Convert to big endian if necessary
306
+ if (pack ('V ' , 65534 ) == pack ('L ' , 65534 )) {
307
+ $ oid .= strrev ($ bin );
308
+ } else {
309
+ $ oid .= $ bin ;
310
+ }
311
+ }
312
+
313
+ return $ oid ;
314
+ }
314
315
}
0 commit comments