summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Raynor <chris@firebase.com>2014-06-25 14:20:33 -0700
committerChris Raynor <chris@firebase.com>2014-06-25 14:20:33 -0700
commitfa7e362eeeec5ee808124626c3f1a612aadbce52 (patch)
treea1c7d6cf401cd7d9d9e2dcce28a831f51c5223f0
parent47fc8a335817834c6ac5b0030097e81468a4a06c (diff)
parentc5e07e0c0e8f7c2b0f33f23482d6672eb7798ed2 (diff)
downloadphp-jwt-fa7e362eeeec5ee808124626c3f1a612aadbce52.zip
php-jwt-fa7e362eeeec5ee808124626c3f1a612aadbce52.tar.gz
php-jwt-fa7e362eeeec5ee808124626c3f1a612aadbce52.tar.bz2
Merge pull request #11 from firebase/BambooHR-master
Bamboo hr master
-rw-r--r--Authentication/JWT.php97
-rw-r--r--tests/JWTTest.php32
2 files changed, 94 insertions, 35 deletions
diff --git a/Authentication/JWT.php b/Authentication/JWT.php
index 65be7a1..38a4f7e 100644
--- a/Authentication/JWT.php
+++ b/Authentication/JWT.php
@@ -13,25 +13,21 @@
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
* @link https://github.com/firebase/php-jwt
*/
-/**
- * JSON Web Token implementation, based on this spec:
- * http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
- *
- * @category Authentication
- * @package Authentication_JWT
- * @author Neuman Vong <neuman@twilio.com>
- * @author Anant Narayanan <anant@php.net>
- * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
- * @link https://github.com/firebase/php-jwt
- */
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 bool $verify Don't skip verification process
+ * @param string $jwt The JWT
+ * @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
* @throws UnexpectedValueException Provided JWT was invalid
@@ -58,7 +54,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 +84,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));
@@ -99,25 +104,61 @@ 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 $method The signing algorithm. Supported
- * algorithms are 'HS256', 'HS384' and 'HS512'
+ * @param string $msg The message to sign
+ * @param string|resource $key The secret key
+ * @param string $method The signing algorithm. Supported 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');
+ }
+ 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');
}
- return hash_hmac($methods[$method], $msg, $key, true);
+ 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..ee131d4 100644
--- a/tests/JWTTest.php
+++ b/tests/JWTTest.php
@@ -29,24 +29,42 @@ class JWTTest extends PHPUnit_Framework_TestCase {
JWT::jsonDecode('this is not valid JSON string');
}
- function testExpiredToken(){
+ function testExpiredToken() {
$this->setExpectedException('UnexpectedValueException');
$payload = array(
- "message"=> "abc",
- "exp"=> time()-20); // time in the past
+ "message" => "abc",
+ "exp" => time() - 20); // time in the past
$encoded = JWT::encode($payload, 'my_key');
JWT::decode($encoded);
}
- function testValidToken(){
+ function testValidToken() {
$payload = array(
- "message"=> "abc",
- "exp"=> time()+20); // time in the future
+ "message" => "abc",
+ "exp" => time() + 20); // time in the future
$encoded = JWT::encode($payload, 'my_key');
$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');
+ }
+
}
?>