26
26
*/
27
27
class JWT
28
28
{
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
+
29
36
/**
30
37
* Decodes a JWT string into a PHP object.
31
38
*
32
39
* @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
34
41
* @param bool $verify Don't skip verification process
35
42
*
36
43
* @return object The JWT's payload as a PHP object
@@ -58,7 +65,14 @@ public static function decode($jwt, $key = null, $verify = true)
58
65
if (empty ($ header ->alg )) {
59
66
throw new DomainException ('Empty algorithm ' );
60
67
}
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 )) {
62
76
throw new UnexpectedValueException ('Signature verification failed ' );
63
77
}
64
78
// Check token expiry time if defined.
@@ -81,10 +95,12 @@ public static function decode($jwt, $key = null, $verify = true)
81
95
* @uses jsonEncode
82
96
* @uses urlsafeB64Encode
83
97
*/
84
- public static function encode ($ payload , $ key , $ algo = 'HS256 ' )
98
+ public static function encode ($ payload , $ key , $ algo = 'HS256 ' , $ keyId = null )
85
99
{
86
100
$ header = array ('typ ' => 'JWT ' , 'alg ' => $ algo );
87
-
101
+ if ($ keyId !== null ) {
102
+ $ header ['kid ' ] = $ keyId ;
103
+ }
88
104
$ segments = array ();
89
105
$ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ header ));
90
106
$ segments [] = JWT ::urlsafeB64Encode (JWT ::jsonEncode ($ payload ));
@@ -100,24 +116,60 @@ public static function encode($payload, $key, $algo = 'HS256')
100
116
* Sign a string with a given key and algorithm.
101
117
*
102
118
* @param string $msg The message to sign
103
- * @param string $key The secret key
119
+ * @param string|resource $key The secret key
104
120
* @param string $method The signing algorithm. Supported
105
- * algorithms are 'HS256', 'HS384' and 'HS512 '
121
+ * algorithms are 'HS256', 'HS384', 'HS512' and 'RS256 '
106
122
*
107
123
* @return string An encrypted message
108
124
* @throws DomainException Unsupported algorithm was specified
109
125
*/
110
126
public static function sign ($ msg , $ key , $ method = 'HS256 ' )
111
127
{
112
- $ methods = array (
113
- 'HS256 ' => 'sha256 ' ,
114
- 'HS384 ' => 'sha384 ' ,
115
- 'HS512 ' => 'sha512 ' ,
116
- );
117
- if (empty ($ methods [$ method ])) {
128
+ if (empty (self ::$ methods [$ method ])) {
118
129
throw new DomainException ('Algorithm not supported ' );
119
130
}
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
+ }
121
173
}
122
174
123
175
/**
0 commit comments