58 lines
1.7 KiB
PHP
58 lines
1.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Security;
|
|
|
|
use Exception;
|
|
|
|
final class EncryptionService
|
|
{
|
|
private string $key;
|
|
private const METHOD = 'aes-256-gcm';
|
|
|
|
public function __construct()
|
|
{
|
|
// Key should be 32 bytes for aes-256-gcm
|
|
$this->key = $_ENV['ENCRYPTION_KEY'] ?? '';
|
|
if (strlen($this->key) !== 32) {
|
|
// In a real app, this would be in config/secrets.php
|
|
// For now, we use a fallback if not set, but warn in production
|
|
$this->key = hash('sha256', $_ENV['JWT_SECRET'] ?? 'fallback-key');
|
|
}
|
|
}
|
|
|
|
public function encrypt(string $plaintext): string
|
|
{
|
|
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(self::METHOD));
|
|
$ciphertext = openssl_encrypt($plaintext, self::METHOD, $this->key, 0, $iv, $tag);
|
|
|
|
if ($ciphertext === false) {
|
|
throw new Exception("Encryption failed.");
|
|
}
|
|
|
|
return base64_encode($iv) . ':' . base64_encode($ciphertext) . ':' . base64_encode($tag);
|
|
}
|
|
|
|
public function decrypt(string $encryptedData): string
|
|
{
|
|
$parts = explode(':', $encryptedData);
|
|
if (count($parts) !== 3) {
|
|
throw new Exception("Invalid encrypted data format.");
|
|
}
|
|
|
|
[$ivBase64, $ciphertextBase64, $tagBase64] = $parts;
|
|
$iv = base64_decode($ivBase64);
|
|
$ciphertext = base64_decode($ciphertextBase64);
|
|
$tag = base64_decode($tagBase64);
|
|
|
|
$plaintext = openssl_decrypt($ciphertext, self::METHOD, $this->key, 0, $iv, $tag);
|
|
|
|
if ($plaintext === false) {
|
|
throw new Exception("Decryption failed.");
|
|
}
|
|
|
|
return $plaintext;
|
|
}
|
|
}
|