import base64 import hashlib from cryptography.fernet import Fernet from flask import current_app class CryptoService: """ Servicio de cifrado simétrico seguro para contraseñas y tokens sensibles almacenados en la base de datos (credenciales SMTP, Client Secrets, Moodle Tokens). """ @staticmethod def _get_fernet() -> Fernet: # Derivar clave válida para Fernet (32 bytes urlsafe base64) desde SECRET_KEY try: secret = current_app.config.get('SECRET_KEY', 'default-unicaba-edu-space-secret-key-32b!') except RuntimeError: secret = 'default-unicaba-edu-space-secret-key-32b!' # Hash SHA-256 para obtener 32 bytes y codificar en base64 seguro para URL key = base64.urlsafe_b64encode(hashlib.sha256(secret.encode('utf-8')).digest()) return Fernet(key) @classmethod def encrypt(cls, plain_text: str) -> str: """Cifra un texto plano y retorna el string cifrado.""" if not plain_text: return '' fernet = cls._get_fernet() encrypted_bytes = fernet.encrypt(plain_text.encode('utf-8')) return encrypted_bytes.decode('utf-8') @classmethod def decrypt(cls, cipher_text: str) -> str: """Descifra un texto cifrado y retorna el texto original. Si falla, retorna vacío.""" if not cipher_text: return '' try: fernet = cls._get_fernet() decrypted_bytes = fernet.decrypt(cipher_text.encode('utf-8')) return decrypted_bytes.decode('utf-8') except Exception: # Si no era un texto cifrado con Fernet o fue alterado, retornar el texto tal cual o vacío return cipher_text crypto_service = CryptoService()