encryptionKey = base64_decode($encryptionKey); if (strlen($this->encryptionKey) !== 32) { throw new RuntimeException("La clé de chiffrement doit faire 32 bytes (256 bits)"); } } /** * Déchiffre les données "searchable" (encrypted_user_name, encrypted_email) * Format: base64 simple avec IV fixe */ public function decryptSearchable(?string $encryptedData): ?string { if (empty($encryptedData)) { return null; } $encrypted = base64_decode($encryptedData); if ($encrypted === false) { return null; } $iv = str_repeat("\0", 16); // IV fixe $decrypted = openssl_decrypt($encrypted, $this->cipher, $this->encryptionKey, 0, $iv); if ($decrypted === false) { return null; } // Supprimer le caractère de contrôle ajouté if (substr($decrypted, -1) === "\x01") { return substr($decrypted, 0, -1); } return $decrypted; } /** * Déchiffre les données avec IV aléatoire (encrypted_name, encrypted_phone, etc.) * Format: base64(IV + encrypted) */ public function decryptWithIV(?string $encryptedData): ?string { if (empty($encryptedData)) { return null; } $data = base64_decode($encryptedData); if ($data === false) { return null; } $ivLength = openssl_cipher_iv_length($this->cipher); if (strlen($data) <= $ivLength) { return null; } $iv = substr($data, 0, $ivLength); $encrypted = substr($data, $ivLength); $decrypted = openssl_decrypt($encrypted, $this->cipher, $this->encryptionKey, 0, $iv); return $decrypted !== false ? $decrypted : null; } /** * Déchiffre une valeur chiffrée * * @param string|null $encryptedValue Valeur chiffrée (format base64:iv:data) * @return string|null Valeur déchiffrée ou null */ public function decrypt(?string $encryptedValue): ?string { if (empty($encryptedValue)) { return null; } // Le format de l'API est : base64:iv:encrypted_data $parts = explode(':', $encryptedValue); if (count($parts) !== 3 || $parts[0] !== 'base64') { // Format invalide, peut-être déjà en clair return $encryptedValue; } $iv = base64_decode($parts[1]); $encrypted = base64_decode($parts[2]); if ($iv === false || $encrypted === false) { throw new RuntimeException("Impossible de décoder les données chiffrées"); } $decrypted = openssl_decrypt( $encrypted, $this->cipher, $this->encryptionKey, OPENSSL_RAW_DATA, $iv ); if ($decrypted === false) { throw new RuntimeException("Échec du déchiffrement : " . openssl_error_string()); } return $decrypted; } /** * Chiffre une valeur * * @param string $value Valeur à chiffrer * @return string Valeur chiffrée (format base64:iv:data) */ public function encrypt(string $value): string { $ivLength = openssl_cipher_iv_length($this->cipher); $iv = openssl_random_pseudo_bytes($ivLength); $encrypted = openssl_encrypt( $value, $this->cipher, $this->encryptionKey, OPENSSL_RAW_DATA, $iv ); if ($encrypted === false) { throw new RuntimeException("Échec du chiffrement : " . openssl_error_string()); } return 'base64:' . base64_encode($iv) . ':' . base64_encode($encrypted); } /** * Déchiffre plusieurs colonnes d'un tableau * * @param array $row Ligne de base de données * @param array $encryptedColumns Liste des colonnes à déchiffrer (sans le préfixe encrypted_) * @return array Tableau avec colonnes déchiffrées */ public function decryptRow(array $row, array $encryptedColumns): array { $decrypted = $row; foreach ($encryptedColumns as $column) { $encryptedColumn = 'encrypted_' . $column; if (isset($row[$encryptedColumn])) { $decrypted[$column] = $this->decrypt($row[$encryptedColumn]); } } return $decrypted; } /** * Déchiffre les colonnes encrypted_* d'un utilisateur * * @param array $user Données utilisateur * @return array Utilisateur avec données déchiffrées */ public function decryptUser(array $user): array { $columns = ['user_name', 'email', 'name', 'phone', 'mobile']; return $this->decryptRow($user, $columns); } /** * Déchiffre les colonnes encrypted_* d'une entité * * @param array $entite Données entité * @return array Entité avec données déchiffrées */ public function decryptEntite(array $entite): array { $columns = ['name', 'email', 'phone', 'mobile', 'iban', 'bic']; return $this->decryptRow($entite, $columns); } }