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)); }
public function verify($signature, $data, $keys, $kid = null) { $compare = $this->sign($data, $keys, $kid); return Util::secure_compare($signature, $compare); }
/** * 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'); } }
/** * 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)); }
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); } }
/** * 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'); } }
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; } }
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; }
/** * 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)); }
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); } }
/** * 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); }
public function shortHash($data) { $hash = hash('sha' . $this->size, $data, true); $short = substr($hash, 0, $this->size / 16); return Util::base64url_encode($short); }
protected function hex2base64url($hex) { return Util::base64url_encode(pack('H*', $hex)); }