| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- <?php
- namespace OAuth2\Encryption;
- use Exception;
- use InvalidArgumentException;
- /**
- * @link https://github.com/F21/jwt
- * @author F21
- */
- class Jwt implements EncryptionInterface
- {
- /**
- * @param $payload
- * @param $key
- * @param string $algo
- * @return string
- */
- public function encode($payload, $key, $algo = 'HS256')
- {
- $header = $this->generateJwtHeader($payload, $algo);
- $segments = array(
- $this->urlSafeB64Encode(json_encode($header)),
- $this->urlSafeB64Encode(json_encode($payload))
- );
- $signing_input = implode('.', $segments);
- $signature = $this->sign($signing_input, $key, $algo);
- $segments[] = $this->urlsafeB64Encode($signature);
- return implode('.', $segments);
- }
- /**
- * @param string $jwt
- * @param null $key
- * @param array|bool $allowedAlgorithms
- * @return bool|mixed
- */
- public function decode($jwt, $key = null, $allowedAlgorithms = true)
- {
- if (!strpos($jwt, '.')) {
- return false;
- }
- $tks = explode('.', $jwt);
- if (count($tks) != 3) {
- return false;
- }
- list($headb64, $payloadb64, $cryptob64) = $tks;
- if (null === ($header = json_decode($this->urlSafeB64Decode($headb64), true))) {
- return false;
- }
- if (null === $payload = json_decode($this->urlSafeB64Decode($payloadb64), true)) {
- return false;
- }
- $sig = $this->urlSafeB64Decode($cryptob64);
- if ((bool) $allowedAlgorithms) {
- if (!isset($header['alg'])) {
- return false;
- }
- // check if bool arg supplied here to maintain BC
- if (is_array($allowedAlgorithms) && !in_array($header['alg'], $allowedAlgorithms)) {
- return false;
- }
- if (!$this->verifySignature($sig, "$headb64.$payloadb64", $key, $header['alg'])) {
- return false;
- }
- }
- return $payload;
- }
- /**
- * @param $signature
- * @param $input
- * @param $key
- * @param string $algo
- * @return bool
- * @throws InvalidArgumentException
- */
- private function verifySignature($signature, $input, $key, $algo = 'HS256')
- {
- // use constants when possible, for HipHop support
- switch ($algo) {
- case'HS256':
- case'HS384':
- case'HS512':
- return $this->hash_equals(
- $this->sign($input, $key, $algo),
- $signature
- );
- case 'RS256':
- return openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256') === 1;
- case 'RS384':
- return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384') === 1;
- case 'RS512':
- return @openssl_verify($input, $signature, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512') === 1;
- default:
- throw new InvalidArgumentException("Unsupported or invalid signing algorithm.");
- }
- }
- /**
- * @param $input
- * @param $key
- * @param string $algo
- * @return string
- * @throws Exception
- */
- private function sign($input, $key, $algo = 'HS256')
- {
- switch ($algo) {
- case 'HS256':
- return hash_hmac('sha256', $input, $key, true);
- case 'HS384':
- return hash_hmac('sha384', $input, $key, true);
- case 'HS512':
- return hash_hmac('sha512', $input, $key, true);
- case 'RS256':
- return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA256') ? OPENSSL_ALGO_SHA256 : 'sha256');
- case 'RS384':
- return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'sha384');
- case 'RS512':
- return $this->generateRSASignature($input, $key, defined('OPENSSL_ALGO_SHA512') ? OPENSSL_ALGO_SHA512 : 'sha512');
- default:
- throw new Exception("Unsupported or invalid signing algorithm.");
- }
- }
- /**
- * @param $input
- * @param $key
- * @param string $algo
- * @return mixed
- * @throws Exception
- */
- private function generateRSASignature($input, $key, $algo)
- {
- if (!openssl_sign($input, $signature, $key, $algo)) {
- throw new Exception("Unable to sign data.");
- }
- return $signature;
- }
- /**
- * @param string $data
- * @return string
- */
- public function urlSafeB64Encode($data)
- {
- $b64 = base64_encode($data);
- $b64 = str_replace(array('+', '/', "\r", "\n", '='),
- array('-', '_'),
- $b64);
- return $b64;
- }
- /**
- * @param string $b64
- * @return mixed|string
- */
- public function urlSafeB64Decode($b64)
- {
- $b64 = str_replace(array('-', '_'),
- array('+', '/'),
- $b64);
- return base64_decode($b64);
- }
- /**
- * Override to create a custom header
- */
- protected function generateJwtHeader($payload, $algorithm)
- {
- return array(
- 'typ' => 'JWT',
- 'alg' => $algorithm,
- );
- }
- /**
- * @param string $a
- * @param string $b
- * @return bool
- */
- protected function hash_equals($a, $b)
- {
- if (function_exists('hash_equals')) {
- return hash_equals($a, $b);
- }
- $diff = strlen($a) ^ strlen($b);
- for ($i = 0; $i < strlen($a) && $i < strlen($b); $i++) {
- $diff |= ord($a[$i]) ^ ord($b[$i]);
- }
- return $diff === 0;
- }
- }
|