diff options
author | Chris Raynor <chris@firebase.com> | 2014-06-25 12:19:32 -0700 |
---|---|---|
committer | Chris Raynor <chris@firebase.com> | 2014-06-25 12:19:32 -0700 |
commit | 3bb58d76599934be52450646cd4fe3ce5619997c (patch) | |
tree | 610541fb59f23d0cc521fea457699c5e65fd38c6 | |
parent | 718e9c732255da4fbd7314319043ebdf14f7cb12 (diff) | |
parent | 026a6d9c45bd0cc702703c06b615e3ec216c4802 (diff) | |
download | php-jwt-3bb58d76599934be52450646cd4fe3ce5619997c.zip php-jwt-3bb58d76599934be52450646cd4fe3ce5619997c.tar.gz php-jwt-3bb58d76599934be52450646cd4fe3ce5619997c.tar.bz2 |
Merge branch 'master' of git://github.com/BambooHR/php-jwt into BambooHR-master
-rw-r--r-- | Authentication/JWT.php | 78 | ||||
-rw-r--r-- | tests/JWTTest.php | 19 |
2 files changed, 84 insertions, 13 deletions
diff --git a/Authentication/JWT.php b/Authentication/JWT.php index 8eefaad..11b1bde 100644 --- a/Authentication/JWT.php +++ b/Authentication/JWT.php @@ -26,11 +26,18 @@ */ class JWT { + static $methods = array( + 'HS256' => array('hash_hmac', 'SHA256'), + 'HS512' => array('hash_hmac', 'SHA512'), + 'HS384' => array('hash_hmac', 'SHA384'), + 'RS256' => array('openssl', 'SHA256'), + ); + /** * Decodes a JWT string into a PHP object. * * @param string $jwt The JWT - * @param string|null $key The secret key + * @param string|Array|null $key The secret key, or map of keys * @param bool $verify Don't skip verification process * * @return object The JWT's payload as a PHP object @@ -58,7 +65,14 @@ class JWT if (empty($header->alg)) { throw new DomainException('Empty algorithm'); } - if ($sig != JWT::sign("$headb64.$bodyb64", $key, $header->alg)) { + if (is_array($key)) { + if(isset($header->kid)) { + $key = $key[$header->kid]; + } else { + throw new DomainException('"kid" empty, unable to lookup correct key'); + } + } + if (!JWT::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { throw new UnexpectedValueException('Signature verification failed'); } // Check token expiry time if defined. @@ -81,10 +95,12 @@ class JWT * @uses jsonEncode * @uses urlsafeB64Encode */ - public static function encode($payload, $key, $algo = 'HS256') + public static function encode($payload, $key, $algo = 'HS256', $keyId = null) { $header = array('typ' => 'JWT', 'alg' => $algo); - + if($keyId !== null) { + $header['kid'] = $keyId; + } $segments = array(); $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); @@ -100,24 +116,60 @@ class JWT * Sign a string with a given key and algorithm. * * @param string $msg The message to sign - * @param string $key The secret key + * @param string|resource $key The secret key * @param string $method The signing algorithm. Supported - * algorithms are 'HS256', 'HS384' and 'HS512' + * algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' * * @return string An encrypted message * @throws DomainException Unsupported algorithm was specified */ public static function sign($msg, $key, $method = 'HS256') { - $methods = array( - 'HS256' => 'sha256', - 'HS384' => 'sha384', - 'HS512' => 'sha512', - ); - if (empty($methods[$method])) { + if (empty(self::$methods[$method])) { throw new DomainException('Algorithm not supported'); } - return hash_hmac($methods[$method], $msg, $key, true); + list($function, $algo) = self::$methods[$method]; + switch($function) { + case 'hash_hmac': + return hash_hmac($algo, $msg, $key, true); + case 'openssl': + $signature = ''; + $success = openssl_sign($msg, $signature, $key, $algo); + if(!$success) { + throw new DomainException("OpenSSL unable to sign data"); + } else { + return $signature; + } + } + } + + /** + * Verify a signature with the mesage, key and method. Not all methods + * are symmetric, so we must have a separate verify and sign method. + * @param string $msg the original message + * @param string $signature + * @param string|resource $key for HS*, a string key works. for RS*, must be a resource of an openssl public key + * @param string $method + * @return bool + * @throws DomainException Invalid Algorithm or OpenSSL failure + */ + public static function verify($msg, $signature, $key, $method = 'HS256') { + if (empty(self::$methods[$method])) { + throw new DomainException('Algorithm not supported'); + } + list($function, $algo) = self::$methods[$method]; + switch($function) { + case 'openssl': + $success = openssl_verify($msg, $signature, $key, $algo); + if(!$success) { + throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string()); + } else { + return $signature; + } + case 'hash_hmac': + default: + return $signature === hash_hmac($algo, $msg, $key, true); + } } /** diff --git a/tests/JWTTest.php b/tests/JWTTest.php index ae65f02..bdd8514 100644 --- a/tests/JWTTest.php +++ b/tests/JWTTest.php @@ -46,6 +46,25 @@ class JWTTest extends PHPUnit_Framework_TestCase { $decoded = JWT::decode($encoded, 'my_key'); $this->assertEquals($decoded->message, 'abc'); } + + function testRSEncodeDecode(){ + $privKey = openssl_pkey_new(array('digest_alg' => 'sha256', + 'private_key_bits' => 1024, + 'private_key_type' => OPENSSL_KEYTYPE_RSA)); + $msg = JWT::encode('abc',$privKey, 'RS256'); + $pubKey = openssl_pkey_get_details($privKey); + $pubKey = $pubKey['key']; + $decoded = JWT::decode($msg, $pubKey, true); + $this->assertEquals($decoded, 'abc'); + } + + function testKIDChooser(){ + $keys = array('1' => 'my_key', '2' => 'my_key2'); + $msg = JWT::encode('abc', $keys['1'], 'HS256', '1'); + $decoded = JWT::decode($msg, $keys, true); + $this->assertEquals($decoded, 'abc'); + } + } |