Ejemplo n.º 1
0
 /**
  * Decodes a serialised JWT.
  *
  * @param string $token the serialised JWT
  * @param SimpleJWT\Keys\KeySet $keys the key set containing the key to verify the
  * JWT's signature
  * @param string $expected_alg the expected value of the `alg` parameter, which
  * should be agreed between the parties out-of-band
  * @param string $kid the ID of the key to use to verify the signature. If null, this
  * is automatically retrieved
  * @param bool|array $skip_validation an array of headers or claims that
  * should be ignored as part of the validation process (e.g. if expired tokens
  * are to be accepted), or false if all validation
  * is to be performed.
  * @param string $format the JWT serialisation format
  * @return JWT the decoded JWT
  * @throws InvalidTokenException if the token is invalid for any reason
  */
 public static function decode($token, $keys, $expected_alg, $kid = null, $skip_validation = array(), $format = self::COMPACT_FORMAT)
 {
     if ($skip_validation === false) {
         $skip_validation = array();
     }
     switch ($format) {
         case self::COMPACT_FORMAT:
             $parts = explode('.', $token, 3);
             if (count($parts) != 3) {
                 throw new InvalidTokenException('Cannot decode compact serialisation', InvalidTokenException::TOKEN_PARSE_ERROR);
             }
             list($protected, $payload, $signature) = $parts;
             break;
         case self::JSON_FORMAT:
             $obj = json_decode($token, true);
             if ($obj == null) {
                 throw new InvalidTokenException('Cannot decode JSON', InvalidTokenException::TOKEN_PARSE_ERROR);
             }
             $payload = $obj['payload'];
             if (isset($obj['signatures'])) {
                 foreach ($obj['signatures'] as $signature_obj) {
                     if (isset($signature_obj['header']['kid'])) {
                         $target_kid = $signature_obj['header']['kid'];
                         if ($target_kid == $kid || $keys->getById($target_kid) != null) {
                             $unprotected = $signature_obj['header'];
                             $protected = $signature_obj['protected'];
                             $signature = $signature_obj['signature'];
                             break;
                         }
                     }
                     throw new InvalidTokenException('Cannot find verifiable signature', InvalidTokenException::TOKEN_PARSE_ERROR);
                 }
             } else {
                 $unprotected = $obj['header'];
                 $protected = $obj['protected'];
                 $signature = $obj['signature'];
             }
             break;
         default:
             throw new \InvalidArgumentException('Incorrect format');
     }
     $headers = json_decode(Util::base64url_decode($protected), true);
     if ($headers == null) {
         throw new InvalidTokenException('Cannot decode header', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     // Process crit
     if (isset($headers['crit'])) {
         foreach ($headers['crit'] as $critical) {
             if (!in_array($critical, array('nbf', 'exp', 'alg', 'kid'))) {
                 throw new InvalidTokenException('Critical header not supported: ' . $critical, InvalidTokenException::UNSUPPORTED_ERROR);
             }
         }
     }
     if (isset($unprotected)) {
         $headers = array_merge($headers, $unprotected);
     }
     $claims = json_decode(Util::base64url_decode($payload), true);
     if ($claims == null) {
         throw new InvalidTokenException('Cannot decode claims', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     // Check signatures
     if ($headers['alg'] != $expected_alg) {
         throw new InvalidTokenException('Unexpected algorithm', InvalidTokenException::SIGNATURE_VERIFICATION_ERROR);
     }
     $signer = AlgorithmFactory::create($expected_alg);
     $signing_input = $protected . '.' . $payload;
     try {
         if (isset($headers['kid'])) {
             $kid = $headers['kid'];
         }
         $result = $signer->verify($signature, $signing_input, $keys, $kid);
     } catch (KeyException $e) {
         throw new InvalidTokenException($e->getMessage(), InvalidTokenException::SIGNATURE_VERIFICATION_ERROR, $e);
     } catch (CryptException $e) {
         throw new InvalidTokenException($e->getMessage(), InvalidTokenException::SIGNATURE_VERIFICATION_ERROR, $e);
     }
     if (!$result) {
         throw new InvalidTokenException('Incorrect signature', InvalidTokenException::SIGNATURE_VERIFICATION_ERROR);
     }
     // Check time, etc
     $time = time();
     if (isset($claims['nbf']) && !in_array('nbf', $skip_validation)) {
         if ($time < $claims['nbf'] - self::$TIME_ALLOWANCE) {
             throw new InvalidTokenException('Too early due to nbf claim', InvalidTokenException::TOO_EARLY_ERROR, null, $claims['nbf']);
         }
     }
     if (isset($claims['exp']) && !in_array('exp', $skip_validation)) {
         if ($time > $claims['exp'] + self::$TIME_ALLOWANCE) {
             throw new InvalidTokenException('Too late due to exp claim', InvalidTokenException::TOO_LATE_ERROR, null, $claims['exp']);
         }
     }
     return new JWT($headers, $claims);
 }
Ejemplo n.º 2
0
 /**
  * Decrypts a JWE.
  *
  * @param string $token the serialised JWE
  * @param SimpleJWT\Keys\KeySet $keys the key set containing the key to verify the
  * JWT's signature
  * @param string $expected_alg the expected value of the `alg` parameter, which
  * should be agreed between the parties out-of-band
  * @param string $format the JWE serialisation format
  * @return JWE the decrypted JWE
  * @throws InvalidTokenException if the token is invalid for any reason
  */
 public static function decrypt($token, $keys, $expected_alg, $format = self::COMPACT_FORMAT)
 {
     switch ($format) {
         case self::COMPACT_FORMAT:
             $parts = explode('.', $token, 5);
             if (count($parts) != 5) {
                 throw new InvalidTokenException('Cannot decode compact serialisation', InvalidTokenException::TOKEN_PARSE_ERROR);
             }
             list($protected, $encrypted_key, $iv, $ciphertext, $tag) = $parts;
             break;
         case self::JSON_FORMAT:
             $obj = json_decode($token, true);
             if ($obj == null) {
                 throw new InvalidTokenException('Cannot decode JSON', InvalidTokenException::TOKEN_PARSE_ERROR);
             }
             $protected = $obj['protected'];
             $unprotected = $obj['unprotected'];
             $iv = $obj['iv'];
             $ciphertext = $obj['ciphertext'];
             $tag = $obj['tag'];
             if (isset($obj['recipients'])) {
                 foreach ($obj['recipients'] as $recipient) {
                     if (isset($recipient_obj['header']['kid'])) {
                         $target_kid = $recipient_obj['header']['kid'];
                         if ($keys->getById($target_kid) != null) {
                             $unprotected = isset($unprotected) ? array_merge($unprotected, $recipient_obj['header']) : $recipient_obj['header'];
                             $encrypted_key = $recipient_obj['encrypted_key'];
                             break;
                         }
                     }
                     throw new InvalidTokenException('Cannot find verifiable signature', InvalidTokenException::TOKEN_PARSE_ERROR);
                 }
             } else {
                 $unprotected = isset($unprotected) ? array_merge($unprotected, $obj['header']) : $obj['header'];
                 $encrypted_key = $obj['encrypted_key'];
             }
             break;
         default:
             throw new \InvalidArgumentException('Incorrect format');
     }
     $headers = json_decode(Util::base64url_decode($protected), true);
     if ($headers == null) {
         throw new InvalidTokenException('Cannot decode header', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     if (isset($unprotected)) {
         $headers = array_merge($headers, $unprotected);
     }
     // Process crit
     if (isset($headers['crit'])) {
         foreach ($headers['crit'] as $critical) {
             if (!in_array($critical, array('alg', 'enc', 'kid', 'zip'))) {
                 throw new InvalidTokenException('Critical header not supported: ' . $critical, InvalidTokenException::UNSUPPORTED_ERROR);
             }
         }
     }
     if (!isset($headers['alg'])) {
         throw new InvalidTokenException('alg parameter missing', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     if (!isset($headers['enc'])) {
         throw new InvalidTokenException('enc parameter missing', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     if ($headers['alg'] != $expected_alg) {
         throw new InvalidTokenException('Unexpected algorithm', InvalidTokenException::DECRYPTION_ERROR);
     }
     $key_enc = AlgorithmFactory::create($headers['alg']);
     $content_enc = AlgorithmFactory::create($headers['enc']);
     if ($key_enc instanceof KeyDerivationAlgorithm) {
         try {
             $agreed_key = $key_enc->deriveKey($keys, $headers, $kid);
         } catch (KeyException $e) {
             throw new InvalidTokenException($e->getMessage(), InvalidTokenException::DECRYPTION_ERROR, $e);
         } catch (CryptException $e) {
             throw new InvalidTokenException($e->getMessage(), InvalidTokenException::DECRYPTION_ERROR, $e);
         }
         if ($key_enc instanceof KeyEncryptionAlgorithm) {
             // Key agreement with wrapping
             $keys->add(new SymmetricKey(array('kty' => SymmetricKey::KTY, 'alg' => $this->headers['alg'], 'k' => Util::base64url_encode($agreed_key), 'kid' => '#key-agreement-with-wrapping'), 'php'));
             $kid = '#key-agreement-with-wrapping';
         } else {
             // Direct key agreement or direct encryption
             $cek = $agreed_key;
             if ($encrypted_key != '') {
                 throw new InvalidTokenException('encrypted key should be empty', InvalidTokenException::TOKEN_PARSE_ERROR);
             }
         }
     }
     if (!isset($cek) && $key_enc instanceof KeyEncryptionAlgorithm) {
         try {
             if (!isset($kid)) {
                 $kid = isset($headers['kid']) ? $headers['kid'] : null;
             }
             $cek = $key_enc->decryptKey($encrypted_key, $keys, $headers, $kid);
         } catch (KeyException $e) {
             throw new InvalidTokenException($e->getMessage(), InvalidTokenException::DECRYPTION_ERROR, $e);
         } catch (CryptException $e) {
             throw new InvalidTokenException($e->getMessage(), InvalidTokenException::DECRYPTION_ERROR, $e);
         }
     }
     if (!$cek) {
         throw new InvalidTokenException('alg parameter incorrect', InvalidTokenException::TOKEN_PARSE_ERROR);
     }
     try {
         $plaintext = $content_enc->decryptAndVerify($ciphertext, $tag, $cek, $protected, $iv);
         if (isset($headers['zip'])) {
             switch ($headers['zip']) {
                 case 'DEF':
                     $plaintext = gzinflate($plaintext);
                     break;
                 default:
                     throw new InvalidTokenException('Unsupported zip header:' . $headers['zip'], InvalidTokenException::UNSUPPORTED_ERROR);
             }
         }
     } catch (CryptException $e) {
         throw new InvalidTokenException($e->getMessage(), InvalidTokenException::DECRYPTION_ERROR, $e);
     }
     return new JWE($headers, $plaintext);
 }