49 lines
1.5 KiB
PHP
49 lines
1.5 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()
|
|
{
|
|
// Load from config/secrets.php — NEVER from .env directly
|
|
$secrets = require dirname(__DIR__, 3) . '/config/secrets.php';
|
|
$key = $secrets['encryption_key'] ?? '';
|
|
|
|
if (strlen($key) !== 32) {
|
|
throw new \RuntimeException(
|
|
'ENCRYPTION_KEY_B64 not set or invalid. ' .
|
|
'Generate: php -r "echo base64_encode(random_bytes(32));"'
|
|
);
|
|
}
|
|
$this->key = $key;
|
|
}
|
|
|
|
public function encrypt(string $plaintext): string
|
|
{
|
|
$iv = random_bytes(12); // 12 bytes for GCM
|
|
$tag = '';
|
|
$ciphertext = openssl_encrypt($plaintext, self::METHOD, $this->key, OPENSSL_RAW_DATA, $iv, $tag, '', 16);
|
|
if ($ciphertext === false) throw new \RuntimeException('Encryption failed');
|
|
return base64_encode($iv) . ':' . base64_encode($ciphertext) . ':' . base64_encode($tag);
|
|
}
|
|
|
|
public function decrypt(string $data): string
|
|
{
|
|
[$iv64, $ct64, $tag64] = explode(':', $data);
|
|
$plaintext = openssl_decrypt(
|
|
base64_decode($ct64), self::METHOD, $this->key,
|
|
OPENSSL_RAW_DATA, base64_decode($iv64), base64_decode($tag64)
|
|
);
|
|
if ($plaintext === false) throw new \RuntimeException('Decryption failed');
|
|
return $plaintext;
|
|
}
|
|
}
|