2626 */
2727class JWT
2828{
29+ static $ methods = array (
30+ 'HS256 ' => array ('hash_hmac ' , 'SHA256 ' ),
31+ 'HS512 ' => array ('hash_hmac ' , 'SHA512 ' ),
32+ 'HS384 ' => array ('hash_hmac ' , 'SHA384 ' ),
33+ 'RS256 ' => array ('openssl ' , 'SHA256 ' ),
34+ );
35+
2936 /**
3037 * Decodes a JWT string into a PHP object.
3138 *
3239 * @param string $jwt The JWT
33- * @param string|null $key The secret key
40+ * @param string|Array| null $key The secret key, or map of keys
3441 * @param bool $verify Don't skip verification process
3542 *
3643 * @return object The JWT's payload as a PHP object
@@ -58,7 +65,14 @@ public static function decode($jwt, $key = null, $verify = true)
5865 if (empty ($ header ->alg )) {
5966 throw new DomainException ('Empty algorithm ' );
6067 }
61- if ($ sig != JWT ::sign ("$ headb64. $ bodyb64 " , $ key , $ header ->alg )) {
68+ if (is_array ($ key )) {
69+ if (isset ($ header ->kid )) {
70+ $ key = $ key [$ header ->kid ];
71+ } else {
72+ throw new DomainException ('"kid" empty, unable to lookup correct key ' );
73+ }
74+ }
75+ if (!JWT ::verify ("$ headb64. $ bodyb64 " , $ sig , $ key , $ header ->alg )) {
6276 throw new UnexpectedValueException ('Signature verification failed ' );
6377 }
6478 // Check token expiry time if defined.
@@ -81,10 +95,12 @@ public static function decode($jwt, $key = null, $verify = true)
8195 * @uses jsonEncode
8296 * @uses urlsafeB64Encode
8397 */
84- public static function encode ($ payload , $ key , $ algo = 'HS256 ' )
98+ public static function encode ($ payload , $ key , $ algo = 'HS256 ' , $ keyId = null )
8599 {
86100 $ header = array ('typ ' => 'JWT ' , 'alg ' => $ algo );
87-
101+ if ($ keyId !== null ) {
102+ $ header ['kid ' ] = $ keyId ;
103+ }
88104 $ segments = array ();
89105 $ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ header ));
90106 $ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ payload ));
@@ -100,24 +116,60 @@ public static function encode($payload, $key, $algo = 'HS256')
100116 * Sign a string with a given key and algorithm.
101117 *
102118 * @param string $msg The message to sign
103- * @param string $key The secret key
119+ * @param string|resource $key The secret key
104120 * @param string $method The signing algorithm. Supported
105- * algorithms are 'HS256', 'HS384' and 'HS512 '
121+ * algorithms are 'HS256', 'HS384', 'HS512' and 'RS256 '
106122 *
107123 * @return string An encrypted message
108124 * @throws DomainException Unsupported algorithm was specified
109125 */
110126 public static function sign ($ msg , $ key , $ method = 'HS256 ' )
111127 {
112- $ methods = array (
113- 'HS256 ' => 'sha256 ' ,
114- 'HS384 ' => 'sha384 ' ,
115- 'HS512 ' => 'sha512 ' ,
116- );
117- if (empty ($ methods [$ method ])) {
128+ if (empty (self ::$ methods [$ method ])) {
118129 throw new DomainException ('Algorithm not supported ' );
119130 }
120- return hash_hmac ($ methods [$ method ], $ msg , $ key , true );
131+ list ($ function , $ algo ) = self ::$ methods [$ method ];
132+ switch ($ function ) {
133+ case 'hash_hmac ' :
134+ return hash_hmac ($ algo , $ msg , $ key , true );
135+ case 'openssl ' :
136+ $ signature = '' ;
137+ $ success = openssl_sign ($ msg , $ signature , $ key , $ algo );
138+ if (!$ success ) {
139+ throw new DomainException ("OpenSSL unable to sign data " );
140+ } else {
141+ return $ signature ;
142+ }
143+ }
144+ }
145+
146+ /**
147+ * Verify a signature with the mesage, key and method. Not all methods
148+ * are symmetric, so we must have a separate verify and sign method.
149+ * @param string $msg the original message
150+ * @param string $signature
151+ * @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key
152+ * @param string $method
153+ * @return bool
154+ * @throws DomainException Invalid Algorithm or OpenSSL failure
155+ */
156+ public static function verify ($ msg , $ signature , $ key , $ method = 'HS256 ' ) {
157+ if (empty (self ::$ methods [$ method ])) {
158+ throw new DomainException ('Algorithm not supported ' );
159+ }
160+ list ($ function , $ algo ) = self ::$ methods [$ method ];
161+ switch ($ function ) {
162+ case 'openssl ' :
163+ $ success = openssl_verify ($ msg , $ signature , $ key , $ algo );
164+ if (!$ success ) {
165+ throw new DomainException ("OpenSSL unable to verify data: " . openssl_error_string ());
166+ } else {
167+ return $ signature ;
168+ }
169+ case 'hash_hmac ' :
170+ default :
171+ return $ signature === hash_hmac ($ algo , $ msg , $ key , true );
172+ }
121173 }
122174
123175 /**
0 commit comments