keyMaterial) !== ''; } public function encrypt(string $plaintext): string { $key = $this->normalizedKey(); $iv = random_bytes(12); $tag = ''; $ciphertext = openssl_encrypt($plaintext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag); if ($ciphertext === false) { throw new RuntimeException('Das Passwort konnte nicht verschlüsselt werden.'); } return base64_encode($iv . $tag . $ciphertext); } public function decrypt(string $encoded): string { $raw = base64_decode($encoded, true); if ($raw === false || strlen($raw) < 28) { throw new RuntimeException('Das gespeicherte Passwort ist beschädigt.'); } $key = $this->normalizedKey(); $iv = substr($raw, 0, 12); $tag = substr($raw, 12, 16); $ciphertext = substr($raw, 28); $plaintext = openssl_decrypt($ciphertext, self::CIPHER, $key, OPENSSL_RAW_DATA, $iv, $tag); if ($plaintext === false) { throw new RuntimeException('Das gespeicherte Passwort konnte nicht entschlüsselt werden.'); } return $plaintext; } private function normalizedKey(): string { if (!$this->isConfigured()) { throw new RuntimeException('REGISTRATION_PASSWORD_KEY ist nicht gesetzt.'); } $trimmed = trim($this->keyMaterial); $decoded = base64_decode($trimmed, true); if ($decoded !== false && strlen($decoded) >= 32) { return substr($decoded, 0, 32); } return hash('sha256', $trimmed, true); } }