Skip to content

Instantly share code, notes, and snippets.

@michitheonlyone
Last active August 30, 2022 09:35
Show Gist options
  • Save michitheonlyone/58ee4efc631c16394533518d6429c8f8 to your computer and use it in GitHub Desktop.
Save michitheonlyone/58ee4efc631c16394533518d6429c8f8 to your computer and use it in GitHub Desktop.
PHP Encryption Class to encrypt strings to encrypted json using openssl library (Stolen from Laravel)
<?php declare(strict_types=1);
// namespace...
final class Encrypter
{
private string $key;
private string $cipher;
const DEFAULT_CIPHER = 'AES-256-CBC';
const ALTERNATIVE_CIPHER = 'AES-128-CBC';
private const CIPHER_LENGTHS = [
self::DEFAULT_CIPHER => 32,
self::ALTERNATIVE_CIPHER => 16,
];
public function __construct(string $base64CodedKey, string $cipher = self::DEFAULT_CIPHER)
{
if (static::supported($base64CodedKey, $cipher)) {
$this->key = base64_decode($base64CodedKey);
$this->cipher = $cipher;
} else {
throw new \Exception('The only supported ciphers are AES-128-CBC and AES-256-CBC with the correct key lengths.');
}
}
private static function supported(string $key, string $cipher): bool
{
$length = strlen(base64_decode($key));
return (in_array($cipher, array_keys(self::CIPHER_LENGTHS)) && self::CIPHER_LENGTHS[$cipher] === $length);
}
public static function generateKey($cipher = self::DEFAULT_CIPHER): string
{
return base64_encode(random_bytes(self::CIPHER_LENGTHS[$cipher]));
}
public function encrypt(string $value): string
{
$initialisationValue = random_bytes(openssl_cipher_iv_length($this->cipher));
$encryptedValue = \openssl_encrypt($value, $this->cipher, $this->key, 0, $initialisationValue);
if (false === $encryptedValue) {
throw new \Exception('Could not encrypt the data.');
}
$mac = $this->hash($initialisationValue = base64_encode($initialisationValue), $encryptedValue);
$json = json_encode(compact('initialisationValue', 'encryptedValue', 'mac'), JSON_UNESCAPED_SLASHES);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new \Exception('Could not encrypt the data.');
}
return base64_encode($json);
}
public function decrypt(string $payload): string
{
$payload = $this->getJsonPayload($payload);
$iv = base64_decode($payload['initialisationValue']);
$decrypted = \openssl_decrypt(
$payload['encryptedValue'], $this->cipher, $this->key, 0, $iv
);
if (false === $decrypted) {
throw new \Exception('Could not decrypt the data.');
}
return $decrypted;
}
protected function hash(string $iv, string $value): string
{
return hash_hmac('sha256', $iv . $value, $this->key);
}
protected function getJsonPayload(string $payload): array
{
$payload = json_decode(base64_decode($payload), true);
if (!$this->validPayload($payload)) {
throw new \Exception('The payload is invalid.');
}
if (!$this->validMac($payload)) {
throw new \Exception('The MAC is invalid.');
}
return $payload;
}
protected function validPayload(array $payload): bool
{
return isset($payload['initialisationValue'], $payload['encryptedValue'], $payload['mac']) &&
strlen(base64_decode($payload['initialisationValue'], true)) === openssl_cipher_iv_length($this->cipher);
}
protected function validMac(array $payload): bool
{
return hash_equals(
$this->hash($payload['initialisationValue'], $payload['encryptedValue']), $payload['mac']
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment