63 lines
1.8 KiB
PHP
63 lines
1.8 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 encryption key from secrets config
|
|
$secrets = require __DIR__ . '/../../../config/secrets.php';
|
|
$this->key = $secrets['encryption_key'] ?? '';
|
|
|
|
// Ensure key is hexadecimal and convert to binary (32 bytes)
|
|
if (strlen($this->key) === 64) {
|
|
$this->key = hex2bin($this->key);
|
|
}
|
|
|
|
if (strlen($this->key) !== 32) {
|
|
throw new Exception("Security Error: Invalid ENCRYPTION_KEY length. Must be 32 bytes.");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|