1313 * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
1414 * @link https://github.com/firebase/php-jwt
1515 */
16- /**
17- * JSON Web Token implementation, based on this spec:
18- * http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
19- *
20- * @category Authentication
21- * @package Authentication_JWT
22- * @author Neuman Vong <neuman@twilio.com>
23- * @author Anant Narayanan <anant@php.net>
24- * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
25- * @link https://github.com/firebase/php-jwt
26- */
2716class JWT
2817{
18+ static $ methods = array (
19+ 'HS256 ' => array ('hash_hmac ' , 'SHA256 ' ),
20+ 'HS512 ' => array ('hash_hmac ' , 'SHA512 ' ),
21+ 'HS384 ' => array ('hash_hmac ' , 'SHA384 ' ),
22+ 'RS256 ' => array ('openssl ' , 'SHA256 ' ),
23+ );
24+
2925 /**
3026 * Decodes a JWT string into a PHP object.
3127 *
32- * @param string $jwt The JWT
33- * @param string|null $key The secret key
34- * @param bool $verify Don't skip verification process
28+ * @param string $jwt The JWT
29+ * @param string|Array| null $key The secret key, or map of keys
30+ * @param bool $verify Don't skip verification process
3531 *
3632 * @return object The JWT's payload as a PHP object
3733 * @throws UnexpectedValueException Provided JWT was invalid
@@ -58,7 +54,14 @@ public static function decode($jwt, $key = null, $verify = true)
5854 if (empty ($ header ->alg )) {
5955 throw new DomainException ('Empty algorithm ' );
6056 }
61- if ($ sig != JWT ::sign ("$ headb64. $ bodyb64 " , $ key , $ header ->alg )) {
57+ if (is_array ($ key )) {
58+ if (isset ($ header ->kid )) {
59+ $ key = $ key [$ header ->kid ];
60+ } else {
61+ throw new DomainException ('"kid" empty, unable to lookup correct key ' );
62+ }
63+ }
64+ if (!JWT ::verify ("$ headb64. $ bodyb64 " , $ sig , $ key , $ header ->alg )) {
6265 throw new UnexpectedValueException ('Signature verification failed ' );
6366 }
6467 // Check token expiry time if defined.
@@ -81,10 +84,12 @@ public static function decode($jwt, $key = null, $verify = true)
8184 * @uses jsonEncode
8285 * @uses urlsafeB64Encode
8386 */
84- public static function encode ($ payload , $ key , $ algo = 'HS256 ' )
87+ public static function encode ($ payload , $ key , $ algo = 'HS256 ' , $ keyId = null )
8588 {
8689 $ header = array ('typ ' => 'JWT ' , 'alg ' => $ algo );
87-
90+ if ($ keyId !== null ) {
91+ $ header ['kid ' ] = $ keyId ;
92+ }
8893 $ segments = array ();
8994 $ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ header ));
9095 $ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ payload ));
@@ -99,25 +104,61 @@ public static function encode($payload, $key, $algo = 'HS256')
99104 /**
100105 * Sign a string with a given key and algorithm.
101106 *
102- * @param string $msg The message to sign
103- * @param string $key The secret key
104- * @param string $method The signing algorithm. Supported
105- * algorithms are 'HS256', 'HS384' and 'HS512 '
107+ * @param string $msg The message to sign
108+ * @param string|resource $key The secret key
109+ * @param string $method The signing algorithm. Supported algorithms
110+ * are 'HS256', 'HS384', 'HS512' and 'RS256 '
106111 *
107112 * @return string An encrypted message
108113 * @throws DomainException Unsupported algorithm was specified
109114 */
110115 public static function sign ($ msg , $ key , $ method = 'HS256 ' )
111116 {
112- $ methods = array (
113- 'HS256 ' => 'sha256 ' ,
114- 'HS384 ' => 'sha384 ' ,
115- 'HS512 ' => 'sha512 ' ,
116- );
117- if (empty ($ methods [$ method ])) {
117+ if (empty (self ::$ methods [$ method ])) {
118+ throw new DomainException ('Algorithm not supported ' );
119+ }
120+ list ($ function , $ algo ) = self ::$ methods [$ method ];
121+ switch ($ function ) {
122+ case 'hash_hmac ' :
123+ return hash_hmac ($ algo , $ msg , $ key , true );
124+ case 'openssl ' :
125+ $ signature = '' ;
126+ $ success = openssl_sign ($ msg , $ signature , $ key , $ algo );
127+ if (!$ success ) {
128+ throw new DomainException ("OpenSSL unable to sign data " );
129+ } else {
130+ return $ signature ;
131+ }
132+ }
133+ }
134+
135+ /**
136+ * Verify a signature with the mesage, key and method. Not all methods
137+ * are symmetric, so we must have a separate verify and sign method.
138+ * @param string $msg the original message
139+ * @param string $signature
140+ * @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key
141+ * @param string $method
142+ * @return bool
143+ * @throws DomainException Invalid Algorithm or OpenSSL failure
144+ */
145+ public static function verify ($ msg , $ signature , $ key , $ method = 'HS256 ' ) {
146+ if (empty (self ::$ methods [$ method ])) {
118147 throw new DomainException ('Algorithm not supported ' );
119148 }
120- return hash_hmac ($ methods [$ method ], $ msg , $ key , true );
149+ list ($ function , $ algo ) = self ::$ methods [$ method ];
150+ switch ($ function ) {
151+ case 'openssl ' :
152+ $ success = openssl_verify ($ msg , $ signature , $ key , $ algo );
153+ if (!$ success ) {
154+ throw new DomainException ("OpenSSL unable to verify data: " . openssl_error_string ());
155+ } else {
156+ return $ signature ;
157+ }
158+ case 'hash_hmac ' :
159+ default :
160+ return $ signature === hash_hmac ($ algo , $ msg , $ key , true );
161+ }
121162 }
122163
123164 /**
0 commit comments