Zur aktuellen Revision | Blame | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Illuminate\Encryption;use Illuminate\Contracts\Encryption\DecryptException;use Illuminate\Contracts\Encryption\Encrypter as EncrypterContract;use Illuminate\Contracts\Encryption\EncryptException;use Illuminate\Contracts\Encryption\StringEncrypter;use RuntimeException;class Encrypter implements EncrypterContract, StringEncrypter{/*** The encryption key.** @var string*/protected $key;/*** The algorithm used for encryption.** @var string*/protected $cipher;/*** The supported cipher algorithms and their properties.** @var array*/private static $supportedCiphers = ['aes-128-cbc' => ['size' => 16, 'aead' => false],'aes-256-cbc' => ['size' => 32, 'aead' => false],'aes-128-gcm' => ['size' => 16, 'aead' => true],'aes-256-gcm' => ['size' => 32, 'aead' => true],];/*** Create a new encrypter instance.** @param string $key* @param string $cipher* @return void** @throws \RuntimeException*/public function __construct($key, $cipher = 'aes-128-cbc'){$key = (string) $key;if (! static::supported($key, $cipher)) {$ciphers = implode(', ', array_keys(self::$supportedCiphers));throw new RuntimeException("Unsupported cipher or incorrect key length. Supported ciphers are: {$ciphers}.");}$this->key = $key;$this->cipher = $cipher;}/*** Determine if the given key and cipher combination is valid.** @param string $key* @param string $cipher* @return bool*/public static function supported($key, $cipher){if (! isset(self::$supportedCiphers[strtolower($cipher)])) {return false;}return mb_strlen($key, '8bit') === self::$supportedCiphers[strtolower($cipher)]['size'];}/*** Create a new encryption key for the given cipher.** @param string $cipher* @return string*/public static function generateKey($cipher){return random_bytes(self::$supportedCiphers[strtolower($cipher)]['size'] ?? 32);}/*** Encrypt the given value.** @param mixed $value* @param bool $serialize* @return string** @throws \Illuminate\Contracts\Encryption\EncryptException*/public function encrypt($value, $serialize = true){$iv = random_bytes(openssl_cipher_iv_length(strtolower($this->cipher)));$value = \openssl_encrypt($serialize ? serialize($value) : $value,strtolower($this->cipher), $this->key, 0, $iv, $tag);if ($value === false) {throw new EncryptException('Could not encrypt the data.');}$iv = base64_encode($iv);$tag = base64_encode($tag ?? '');$mac = self::$supportedCiphers[strtolower($this->cipher)]['aead']? '' // For AEAD-algoritms, the tag / MAC is returned by openssl_encrypt...: $this->hash($iv, $value);$json = json_encode(compact('iv', 'value', 'mac', 'tag'), JSON_UNESCAPED_SLASHES);if (json_last_error() !== JSON_ERROR_NONE) {throw new EncryptException('Could not encrypt the data.');}return base64_encode($json);}/*** Encrypt a string without serialization.** @param string $value* @return string** @throws \Illuminate\Contracts\Encryption\EncryptException*/public function encryptString($value){return $this->encrypt($value, false);}/*** Decrypt the given value.** @param string $payload* @param bool $unserialize* @return mixed** @throws \Illuminate\Contracts\Encryption\DecryptException*/public function decrypt($payload, $unserialize = true){$payload = $this->getJsonPayload($payload);$iv = base64_decode($payload['iv']);$this->ensureTagIsValid($tag = empty($payload['tag']) ? null : base64_decode($payload['tag']));// Here we will decrypt the value. If we are able to successfully decrypt it// we will then unserialize it and return it out to the caller. If we are// unable to decrypt this value we will throw out an exception message.$decrypted = \openssl_decrypt($payload['value'], strtolower($this->cipher), $this->key, 0, $iv, $tag ?? '');if ($decrypted === false) {throw new DecryptException('Could not decrypt the data.');}return $unserialize ? unserialize($decrypted) : $decrypted;}/*** Decrypt the given string without unserialization.** @param string $payload* @return string** @throws \Illuminate\Contracts\Encryption\DecryptException*/public function decryptString($payload){return $this->decrypt($payload, false);}/*** Create a MAC for the given value.** @param string $iv* @param mixed $value* @return string*/protected function hash($iv, $value){return hash_hmac('sha256', $iv.$value, $this->key);}/*** Get the JSON array from the given payload.** @param string $payload* @return array** @throws \Illuminate\Contracts\Encryption\DecryptException*/protected function getJsonPayload($payload){$payload = json_decode(base64_decode($payload), true);// If the payload is not valid JSON or does not have the proper keys set we will// assume it is invalid and bail out of the routine since we will not be able// to decrypt the given value. We'll also check the MAC for this encryption.if (! $this->validPayload($payload)) {throw new DecryptException('The payload is invalid.');}if (! self::$supportedCiphers[strtolower($this->cipher)]['aead'] && ! $this->validMac($payload)) {throw new DecryptException('The MAC is invalid.');}return $payload;}/*** Verify that the encryption payload is valid.** @param mixed $payload* @return bool*/protected function validPayload($payload){if (! is_array($payload)) {return false;}foreach (['iv', 'value', 'mac'] as $item) {if (! isset($payload[$item]) || ! is_string($payload[$item])) {return false;}}if (isset($payload['tag']) && ! is_string($payload['tag'])) {return false;}return strlen(base64_decode($payload['iv'], true)) === openssl_cipher_iv_length(strtolower($this->cipher));}/*** Determine if the MAC for the given payload is valid.** @param array $payload* @return bool*/protected function validMac(array $payload){return hash_equals($this->hash($payload['iv'], $payload['value']), $payload['mac']);}/*** Ensure the given tag is a valid tag given the selected cipher.** @param string $tag* @return void*/protected function ensureTagIsValid($tag){if (self::$supportedCiphers[strtolower($this->cipher)]['aead'] && strlen($tag) !== 16) {throw new DecryptException('Could not decrypt the data.');}if (! self::$supportedCiphers[strtolower($this->cipher)]['aead'] && is_string($tag)) {throw new DecryptException('Unable to use tag because the cipher algorithm does not support AEAD.');}}/*** Get the encryption key that the encrypter is currently using.** @return string*/public function getKey(){return $this->key;}}