Exemple #1
0
 function testPBES2Decrypt()
 {
     $encrypted_key = 'TrqXOwuNUfDV9VPTNbyGvEJ9JMjefAVn-TR1uIxR9p6hsRQh9Tk7BA';
     $password = '******';
     $keys = $this->getKeySet($password);
     $headers = array('alg' => 'PBES2-HS256+A128KW', 'p2c' => 4096, 'p2s' => '2WCTcJZ1Rvd_CJuJripQ1w');
     $alg = new PBES2('PBES2-HS256+A128KW');
     $cek = $alg->decryptKey($encrypted_key, $keys, $headers);
     $this->assertEquals('bxsZNEIdFE5csDjwQdBScKGDJDfK7LmsgReZwsMw_bY', Util::base64url_encode($cek));
 }
Exemple #2
0
 public function verify($signature, $data, $keys, $kid = null)
 {
     $compare = $this->sign($data, $keys, $kid);
     return Util::secure_compare($signature, $compare);
 }
Exemple #3
0
 /**
  * Signs and serialises the JWT.
  *
  * @param SimpleJWT\Keys\KeySet $keys the key set containing the key to sign the
  * JWT
  * @param string $kid the ID of the key to use to sign. If null, this
  * is automatically retrieved
  * @param bool|array $auto_complete an array of headers or claims that
  * should be automatically completed, or false if no auto-completion is
  * to be performed
  * @param string $alg if not null, override the `alg` header
  * @param string $format the JWT serialisation format
  * @return string the signed and serialised JWT
  * @throws SimpleJWT\Keys\KeyException if there is an error obtaining the key
  * to sign the JWT
  * @throws SimpleJWT\Crypt\CryptException if there is a cryptographic error
  */
 public function encode($keys, $kid = null, $auto_complete = array('iat', 'kid'), $alg = null, $format = self::COMPACT_FORMAT)
 {
     if ($auto_complete === false) {
         $auto_complete = array();
     }
     if ($alg != null) {
         $this->headers['alg'] = $alg;
     }
     if (in_array('iat', $auto_complete) && !isset($this->claims['iat'])) {
         $this->claims['iat'] = time();
     }
     try {
         $signer = AlgorithmFactory::create($this->headers['alg']);
     } catch (\UnexpectedValueException $e) {
         throw new CryptException($e->getMessage(), 0, $e);
     }
     $key = $signer->getSigningKey($keys, $kid);
     if ($key != null && in_array('kid', $auto_complete)) {
         $this->headers['kid'] = $key->getKeyId();
     }
     $protected = Util::base64url_encode(json_encode($this->headers));
     $payload = Util::base64url_encode(json_encode($this->claims));
     $signing_input = $protected . '.' . $payload;
     $signature = $signer->sign($signing_input, $keys, $kid);
     switch ($format) {
         case self::COMPACT_FORMAT:
             return $signing_input . '.' . $signature;
         case self::JSON_FORMAT:
             return json_encode(array('protected' => $protected, 'payload' => $payload, 'signature' => $signature));
         default:
             throw new \InvalidArgumentException('Incorrect format');
     }
 }
Exemple #4
0
 /**
  * Returns the symmetric key in binary representation
  *
  * @return string the key
  */
 public function toBinary()
 {
     return Util::base64url_decode($this->data['k']);
 }
 public function decryptAndVerify($ciphertext, $tag, $cek, $additional, $iv)
 {
     $params = self::$alg_params[$this->getAlg()];
     if (strlen($cek) != $this->getCEKSize() / 8) {
         throw new CryptException('Incorrect key length');
     }
     $iv = Util::base64url_decode($iv);
     if (strlen($iv) != $this->getIVSize() / 8) {
         throw new CryptException('Incorrect IV length');
     }
     list($mac_key, $enc_key) = str_split($cek, (int) (strlen($cek) / 2));
     $al = Util::packInt64(strlen($additional) * 8);
     $e = Util::base64url_decode($ciphertext);
     $m = hash_hmac($params['hash'], $additional . $iv . $e . $al, $mac_key, true);
     $t = substr($m, 0, $params['tag']);
     if (!Util::secure_compare(Util::base64url_decode($tag), $t)) {
         throw new CryptException('Authentication tag does not match');
     }
     $plaintext = openssl_decrypt($e, $params['cipher'], $enc_key, OPENSSL_RAW_DATA, $iv);
     return $plaintext;
 }
 protected function base64url($base64)
 {
     return Util::base64url_encode(base64_decode($base64));
 }
Exemple #7
0
 public function toPEM()
 {
     if ($this->isPublic()) {
         $der = ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::OID, ASN1::encodeOID(self::OID)) . ASN1::encodeDER(ASN1::NULL_TYPE), false) . ASN1::encodeDER(ASN1::BIT_STRING, chr(0x0) . ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['n'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['e'])), false)), false);
         return wordwrap("-----BEGIN PUBLIC KEY-----\n" . base64_encode($der) . "\n-----END PUBLIC KEY-----\n", 64, "\n", true);
     } else {
         $der = ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::INTEGER_TYPE, chr(0)) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['n'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['e'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['d'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['p'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['q'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['dp'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['dq'])) . ASN1::encodeDER(ASN1::INTEGER_TYPE, Util::base64url_decode($this->data['qi'])), false);
         return wordwrap("-----BEGIN RSA PRIVATE KEY-----\n" . base64_encode($der) . "\n-----END RSA PRIVATE KEY-----\n", 64, "\n", true);
     }
 }
Exemple #8
0
 /**
  * Encrypts the JWE.
  *
  * @param SimpleJWT\Keys\KeySet $keys the key set containing the key to encrypt the
  * content encryption key
  * @param string $kid the ID of the key to use to encrypt. If null, this
  * is automatically retrieved
  * @param string $format the JWE serialisation format
  * @return string the encrypted JWE
  * @throws SimpleJWT\Keys\KeyException if there is an error obtaining the key
  * to sign the JWT
  * @throws SimpleJWT\Crypt\CryptException if there is a cryptographic error
  */
 public function encrypt($keys, $kid = null, $format = self::COMPACT_FORMAT)
 {
     if (!isset($this->headers['alg'])) {
         throw new \InvalidArgumentException('alg parameter missing');
     }
     if (!isset($this->headers['enc'])) {
         throw new \InvalidArgumentException('enc parameter missing');
     }
     $key_enc = AlgorithmFactory::create($this->headers['alg']);
     $content_enc = AlgorithmFactory::create($this->headers['enc']);
     if ($kid != null) {
         $this->headers['kid'] = $kid;
     }
     if ($key_enc instanceof KeyDerivationAlgorithm) {
         $agreed_key = $key_enc->deriveKey($keys, $this->headers, $kid);
         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)), 'php'));
         } else {
             // Direct key agreement or direct encryption
             $cek = $agreed_key;
         }
     }
     if (!isset($cek)) {
         $cek = Util::random_bytes($content_enc->getCEKSize() / 8);
     }
     if ($key_enc instanceof KeyEncryptionAlgorithm) {
         $encrypted_key = $key_enc->encryptKey($cek, $keys, $this->headers, $kid);
     } else {
         $encrypted_key = '';
     }
     if (isset($this->headers['zip'])) {
         switch ($this->headers['zip']) {
             case 'DEF':
                 $plaintext = gzdeflate($this->plaintext);
                 break;
             default:
                 throw new \InvalidArgumentException('Unsupported zip header:' . $this->headers['zip']);
         }
     } else {
         $plaintext = $this->plaintext;
     }
     $protected = Util::base64url_encode(json_encode($this->headers));
     $results = $content_enc->encryptAndSign($plaintext, $cek, $protected);
     $ciphertext = $results['ciphertext'];
     $iv = isset($results['iv']) ? $results['iv'] : '';
     $tag = $results['tag'];
     switch ($format) {
         case self::COMPACT_FORMAT:
             return $protected . '.' . $encrypted_key . '.' . $iv . '.' . $ciphertext . '.' . $tag;
         case self::JSON_FORMAT:
             $obj = array('protected' => $protected, 'ciphertext' => $ciphertext, 'tag' => $tag, 'encrypted_key' => $encrypted_key);
             if ($iv) {
                 $obj['iv'] = $iv;
             }
             return json_encode($obj);
         default:
             throw new \InvalidArgumentException('Incorrect format');
     }
 }
Exemple #9
0
 public function verify($signature, $data, $keys, $kid = null)
 {
     $key = $this->selectKey($keys, $kid, array(Key::PUBLIC_PROPERTY => true, '~use' => 'sig'));
     if ($key == null) {
         throw new KeyException('Key not found or is invalid');
     }
     $binary = Util::base64url_decode($signature);
     if ($key->getKeyType() == \SimpleJWT\Keys\ECKey::KTY) {
         // For ECDSA signatures, OpenSSL expects a ASN.1 DER SEQUENCE
         list($r, $s) = str_split($binary, (int) (strlen($binary) / 2));
         // Trim leading zeros
         $r = ltrim($r, "");
         $s = ltrim($s, "");
         // Convert r and s from unsigned big-endian integers to signed two's complement
         if (ord($r[0]) > 0x7f) {
             $r = "" . $r;
         }
         if (ord($s[0]) > 0x7f) {
             $s = "" . $s;
         }
         $binary = ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::INTEGER_TYPE, $r) . ASN1::encodeDER(ASN1::INTEGER_TYPE, $s), false);
     }
     $result = openssl_verify($data, $binary, $key->toPEM(), 'SHA' . $this->size);
     switch ($result) {
         case 1:
             return true;
             break;
         case 0:
             return false;
             break;
         default:
             $messages = array();
             while ($message = openssl_error_string()) {
                 $messages[] = $message;
             }
             throw new CryptException('Cannot verify signature: ' . implode("\n", $messages));
             return false;
             break;
     }
 }
Exemple #10
0
 private function getKeySetFromPassword($password, $headers)
 {
     $salt = $headers['alg'] . "" . Util::base64url_decode($headers['p2s']);
     $hash = hash_pbkdf2($this->hash_alg, $password, $salt, $headers['p2c'], $this->getAESKWKeySize() / 8, true);
     $keys = new KeySet();
     $keys->add(new SymmetricKey($hash, 'bin'));
     return $keys;
 }
Exemple #11
0
 /**
  * Obtains a signature for the key.  The signature is derived from the
  * keys to the JSON web key object as returned by the {@link getSignatureKeys()}
  * function.
  *
  * For asymmetric keys, the public and private keys should have the same
  * signature.
  *
  * @return string the signature
  */
 public function getSignature()
 {
     $keys = $this->getSignatureKeys();
     $signing = array();
     foreach ($keys as $key) {
         $signing[$key] = $this->data[$key];
     }
     ksort($signing);
     return Util::base64url_encode(hash('sha256', json_encode($signing), true));
 }
Exemple #12
0
 public function toPEM()
 {
     $oid = $this->getOID($this->data['crv']);
     if ($oid == null) {
         throw new KeyException('Unrecognised EC curve');
     }
     if ($this->isPublic()) {
         $der = ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::OID, ASN1::encodeOID(self::EC_OID)) . ASN1::encodeDER(ASN1::OID, ASN1::encodeOID($oid)), false) . ASN1::encodeDER(ASN1::BIT_STRING, chr(0x0) . chr(0x4) . Util::base64url_decode($this->data['x']) . Util::base64url_decode($this->data['y'])), false);
         return wordwrap("-----BEGIN PUBLIC KEY-----\n" . base64_encode($der) . "\n-----END PUBLIC KEY-----\n", 64, "\n", true);
     } else {
         $der = ASN1::encodeDER(ASN1::SEQUENCE, ASN1::encodeDER(ASN1::INTEGER_TYPE, chr(0x1)) . ASN1::encodeDER(ASN1::OCTET_STRING, Util::base64url_decode($this->data['d'])) . ASN1::encodeDER(0x0, ASN1::encodeDER(ASN1::OID, ASN1::encodeOID($oid)), false, ASN1::CONTEXT_CLASS) . ASN1::encodeDER(0x1, ASN1::encodeDER(ASN1::BIT_STRING, chr(0x0) . chr(0x4) . Util::base64url_decode($this->data['x']) . Util::base64url_decode($this->data['y'])), false, ASN1::CONTEXT_CLASS), false);
         return wordwrap("-----BEGIN EC PRIVATE KEY-----\n" . base64_encode($der) . "\n-----END EC PRIVATE KEY-----\n", 64, "\n", true);
     }
 }
Exemple #13
0
 /**
  * Decodes a message using EME-OAEP.
  *
  * @param string $message the message to decode
  * @param int $key_length the length of the RSA key in octets
  * @param string $hash the hash algorithm - must be one supported by `hash_algos()`
  * @param string $label the label
  * @return string the decoded message
  * @throws CryptException if an error occurred in the decoding
  * @see https://tools.ietf.org/html/rfc3447
  */
 protected final function oaep_decode($encoded, $key_length, $hash = 'sha1', $label = '')
 {
     $lHash = hash($hash, $label, true);
     $Y = ord($encoded[0]);
     $maskedSeed = substr($encoded, 1, strlen($lHash));
     $maskedDB = substr($encoded, strlen($lHash) + 1);
     $seedMask = $this->mgf1($maskedDB, strlen($lHash), $hash);
     $seed = $maskedSeed ^ $seedMask;
     $dbMask = $this->mgf1($seed, $key_length - strlen($lHash) - 1, $hash);
     $DB = $maskedDB ^ $dbMask;
     $lHash2 = substr($DB, 0, strlen($lHash));
     if (!Util::secure_compare($lHash, $lHash2)) {
         throw new CryptException('OAEP decoding error');
     }
     $PSM = substr($DB, strlen($lHash));
     $PSM = ltrim($PSM, "");
     if (substr($PSM, 0, 1) != "") {
         throw new CryptException('OAEP decoding error');
     }
     return substr($PSM, 1);
 }
Exemple #14
0
 public function shortHash($data)
 {
     $hash = hash('sha' . $this->size, $data, true);
     $short = substr($hash, 0, $this->size / 16);
     return Util::base64url_encode($short);
 }
Exemple #15
0
 protected function hex2base64url($hex)
 {
     return Util::base64url_encode(pack('H*', $hex));
 }