Tutorial: Encriptar un archivo en .Net y uso de FileSystemWatcher

Introducción:

En criptografía un cifrado, es un procedimiento que utilizando un algoritmo con cierta clave transforma un mensaje, sin atender a su estructura lingüística o significado, de tal forma que sea incomprensible o, al menos, difícil de comprender, a toda persona que no tenga la clave secreta del algoritmo que se usa para poder descifrarlo. Por tanto tenemos dos algoritmos (el de cifrado y el de descifrado) y dos claves (clave de cifrado y clave de descifrado). Estas dos claves pueden ser iguales (criptografía simétrica) o no (criptografía asimétrica).

Las diferencias entre ambas son en resumen:

Simétrica: 
  • Una misma clave para cifrado y decifrado.
  • Es más rápido.


Asimétrica:
  • Una clave pública para cifrar.
  • Una clave privada para decifrar.
  • Es más lento.
  • Es más seguro que los algoritmos simétricos.


La clase FileSystemWatcher

Escucha las notificaciones de cambio del sistema de archivos y provoca eventos cuando cambia un directorio o un archivo de un directorio. En otras palabras es un monitor de archivos.


Ejercicio práctico:

Para este tutorial de encriptar y desencriptar un archivo en .net, usaremos lo siguiente:
  • Visual Studio 2010
  • Lenguaje C#
  • Algoritmo de cifrado y decifrado DES
  • Este algoritmo necesita una clave de 8 bytes ejm: "12345678", "abcdefgh", "clave123"
  • Clase FileSystemWatcher


1. Lo primero será crear una aplicación de Consola en C# y darle nombre "ConsoleApplicationSeguro_SLN".

2. Crear 2 clases y nombrarlas: "Encriptar.cs" y "Watcher.cs".

3. La clase Watcher será nuestro monitor de archivos y tendrá el siguiente código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace ConsoleApplicationSeguro
{
    public class Watcher
    {
        public string llave { get; set; }
        public void Run(string ruta)
        {
            Console.WriteLine("Directorio a monitorear: " + ruta);

            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = ruta;

            // Monitorea cambios como último acceso y última modificación de archivos y directorios.
            watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
            
            // Seteamos el filtro para todos los archivos.
            watcher.Filter = "*.*";

            // Agrego manejadores de eventos "Handlers".
            watcher.Created += new FileSystemEventHandler(OnCreated);

            // Empieza el monitoreo.
            watcher.EnableRaisingEvents = true;

            // Preguntar si desea salir del monitoreo.
            Console.WriteLine("Presione \'q\' para salir del monitoreo.");
            while (Console.ReadLine() != "q") ;
        }

        // Definir los handlers.
        private void OnCreated(object source, FileSystemEventArgs e)
        {
            // Instrucciones que se debe realizar cuando se dispara el evento:
            Encriptar en = new Encriptar();
            Console.WriteLine("Archivo: " + e.FullPath);
            
            if (e.FullPath.Substring(e.FullPath.Length - 3, 3).ToString() != "cfr")
            {
                Console.WriteLine("Encriptando el archivo: " + e.FullPath);
                FileStream archivo = new FileStream(e.FullPath, FileMode.Open, FileAccess.ReadWrite);
                en.CifrarArchivo(archivo, llave);
            }
        }
    }
}

La clase "Encriptar.cs" se encarga de cifrar y decifrar archivos de todo tipo con el siguiente código:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace ConsoleApplicationSeguro
{
    public class Encriptar
    {
        #region "Cifrado de Archivos"
        public void CifrarArchivo(FileStream archivo, string key)
        {
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();

            byte[] keyBytes = Encoding.UTF8.GetBytes(key);
            des.Key = keyBytes;
            
            key = Convert.ToBase64String(keyBytes);

            des.Mode = CipherMode.ECB;

            ICryptoTransform encryptor = des.CreateEncryptor();

            //crea un byte array de longitud del mismo archivo
            byte[] archivoBytes = new byte[archivo.Length]; 
            archivo.Read(archivoBytes,0,(int)archivo.Length); 
            archivo.Close();

            //encriptando
            byte[] dataEncrypted = encryptor.TransformFinalBlock(archivoBytes, 0, archivoBytes.Length);

            FileStream archivoEncriptado = new FileStream(archivo.Name + ".cfr", FileMode.Create, FileAccess.Write);
            archivoEncriptado.Write(dataEncrypted, 0, (int)dataEncrypted.Length);
            archivoEncriptado.Close();
        }


        public void DecifrarArchivos(FileStream archivo, string key, string rutaDestino, ref string mensaje)
        {
            try
            {
                mensaje = "";

                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                
                byte[] keyBytes = Encoding.UTF8.GetBytes(key);
                key = Convert.ToBase64String(keyBytes);
                          
                des.Key = Convert.FromBase64String(key);

                des.Mode = CipherMode.ECB;

                ICryptoTransform decryptor = des.CreateDecryptor();

                //crea un byte array de longitud del mismo archivo
                byte[] archivoBytes = new byte[archivo.Length];
                archivo.Read(archivoBytes, 0, (int)archivo.Length);
                archivo.Close();

                //Decriptando
                byte[] dataDecrypted = decryptor.TransformFinalBlock(archivoBytes, 0, archivoBytes.Length);

                FileStream archivoDesEncriptado = new FileStream(rutaDestino + Path.GetFileName(archivo.Name.Substring(0, archivo.Name.Length - 4)), FileMode.Create, FileAccess.Write);
                archivoDesEncriptado.Write(dataDecrypted, 0, (int)dataDecrypted.Length);
                archivoDesEncriptado.Close();

                mensaje = "Descifrado correctamente";
            }
            catch (CryptographicException ex)
            {
                mensaje = ex.Message;
            }
            catch (ArgumentException ex)
            {
                mensaje = ex.Message;
            }
            
        }

        #endregion

        public bool validaKey(string llave)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(llave);
            if (keyBytes.Length == 8)
                return true;
            else
                return false;
        } 
    }
}

Bien ahora solo falta llamar a las funciones en el Main desde la clase "Program.cs":

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplicationSeguro
{
    class Program
    {
        static void Main(string[] args)
        {
            Encriptar en = new Encriptar();
            Watcher w = new Watcher();

            string llave;
            string rutaOrigen = "K:/Seguro/";
            string rutaDestino = "K:/Inseguro/";
            string mensaje = "";

            Console.WriteLine("******* SISTEMA DE CIFRADO Y DESCIFRADO WILLSECURITY *******");

            Console.Write("\n Ingrese la llave para la encriptación: ");
            llave = Console.ReadLine();
            while (en.validaKey(llave) == false)
            {
                Console.WriteLine("La llave ingresada no es soportada");
                Console.Write("Ingrese la llave:");
                llave = Console.ReadLine();
            }
            w.llave = llave;

            //Aqui empieza a monitorear eventos de creacion de archivos en el directorio
            w.Run(rutaOrigen);
            Console.WriteLine("Encriptaciòn Completada");
            
            //Aqui Pregunta si deseas desencriptar
            Console.Write("Desea desencriptar archivos? s/n : ");
            while (Console.ReadLine() == "s")
            {
                Console.Write("Nombre de archivo: ");
                string archivo = rutaOrigen + Console.ReadLine();

                Console.Write("Clave de descifrado: ");
                string clave = Console.ReadLine();

                
                if (File.Exists(archivo))
                {
                    FileStream archivoCifrado = new FileStream(archivo, FileMode.Open, FileAccess.ReadWrite);
                    Console.WriteLine("Desencriptando el archivo: " + archivo);
                    en.DecifrarArchivos(archivoCifrado, clave, rutaDestino, ref mensaje);
                    Console.WriteLine(mensaje);
                }
                else
                    Console.WriteLine("Archivo no existe");

                Console.Write("Desea desencriptar otro archivo? S/N : ");
            }
            Console.ReadKey();
        }

    }
}

Compilarlo y ejecutarlo.

A continuación algunas imágenes de la ejecución:

1. La aplicación solicita llave para cifrar, recordar que debe ser de 8 bytes

2. Nos indica que está monitoreando el directorio "K:/Seguro/"



 3. Copiar cualquier archivo a ese directorio, en este caso copié y pegué un archivo PDF de mi tesis. Automáticamente el watcher se da cuenta de que hubo cambios en ese directorio, y dispara el evento respectivo, el cual crea un archivo encriptado a partir del original.

4. Presionamos "q" para salir del monitoreo. Y nos pregunta si queremos decifrar algún archivo. Tecleamos "s" y damos enter


5. Nos solicita el nombre del archivo .cfr y la llave de decifrado, recordar que para este algoritmo la llave de cifrado y decifrado es la misma porque es un algoritmo simétrico.
Automáticamente procederá a decifrar el archivo y lo pondrá en el directorio seteado "K:/Inseguro/"


6. Para finalizar la aplicación teclear "n" y dar enter.

En conclusión:

La encriptación, es un método para el traspaso de información sensible y existen variedades de algoritmos para cada caso según la lógica del negocio, el programador deberá elegir el algoritmo que más le convenga a la empresa.

El uso de la clase FileSystemWatcher, puede ser un excelente monitor de archivos y se puede combinar con otras técnicas como la encriptación.

Comentarios