13
13
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
14
14
* @link https://github.com/firebase/php-jwt
15
15
*/
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 <[email protected] >
23
- * @author Anant Narayanan <[email protected] >
24
- * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
25
- * @link https://github.com/firebase/php-jwt
26
- */
27
16
class JWT
28
17
{
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
+
29
25
/**
30
26
* Decodes a JWT string into a PHP object.
31
27
*
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
35
31
*
36
32
* @return object The JWT's payload as a PHP object
37
33
* @throws UnexpectedValueException Provided JWT was invalid
@@ -58,7 +54,14 @@ public static function decode($jwt, $key = null, $verify = true)
58
54
if (empty ($ header ->alg )) {
59
55
throw new DomainException ('Empty algorithm ' );
60
56
}
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 )) {
62
65
throw new UnexpectedValueException ('Signature verification failed ' );
63
66
}
64
67
// Check token expiry time if defined.
@@ -81,10 +84,12 @@ public static function decode($jwt, $key = null, $verify = true)
81
84
* @uses jsonEncode
82
85
* @uses urlsafeB64Encode
83
86
*/
84
- public static function encode ($ payload , $ key , $ algo = 'HS256 ' )
87
+ public static function encode ($ payload , $ key , $ algo = 'HS256 ' , $ keyId = null )
85
88
{
86
89
$ header = array ('typ ' => 'JWT ' , 'alg ' => $ algo );
87
-
90
+ if ($ keyId !== null ) {
91
+ $ header ['kid ' ] = $ keyId ;
92
+ }
88
93
$ segments = array ();
89
94
$ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ header ));
90
95
$ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ payload ));
@@ -99,25 +104,61 @@ public static function encode($payload, $key, $algo = 'HS256')
99
104
/**
100
105
* Sign a string with a given key and algorithm.
101
106
*
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 '
106
111
*
107
112
* @return string An encrypted message
108
113
* @throws DomainException Unsupported algorithm was specified
109
114
*/
110
115
public static function sign ($ msg , $ key , $ method = 'HS256 ' )
111
116
{
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 ])) {
118
147
throw new DomainException ('Algorithm not supported ' );
119
148
}
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
+ }
121
162
}
122
163
123
164
/**
0 commit comments