Encriptar y desencriptar un archivo con Rijndael en ASP.NET MVC.
Este post es una guía paso a paso de cómo crear un proyecto ASP.NET MVC funcional, el cual nos permitirá encriptar y desencriptar de un archivo.
Lo primero de todo es abrir Visual Studios ycrear un nuevo proyecto.
Seleccionamos Vacío (Empty) y MVC.
El Modelo
Una vez creado nuestro proyecto, sobre la carpeta Models, añadiremos una clase, la cual va ha ser la encargada de cifrar y descifrar los archivos. En mi caso he llamado a esta clase “Crypt”.
Una vez tenemos la clase creada, deberemos añadir los recursos que vamos a utilizar.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
Añadiremos los métodos que nos permiten codificar las claves (convertir a byte[]) para que tengan el formato establecido por la codificación Rijndael.
public static byte[] EncodingPrivateKey(String privada)
{
byte[] key = UTF8Encoding.UTF8.GetBytes(privada);
int keySize = 32;
Array.Resize(ref key, keySize);
return key;
}
public static byte[] EncodingPublicKey(String publica)
{
byte[] iv = UTF8Encoding.UTF8.GetBytes(publica);
int ivSize = 16;
Array.Resize(ref iv, ivSize);
return iv;
}
Ahora crearemos los métodos que se encargarán de encriptar y desencriptar los ficheros que se crearán.
El método “EncryptToFile” contendrá cuatro parámetros:
-El texto que guardaremos en el fichero(“plainMessage”).
-La ruta mapeada más el nombre del archivo a generar(“filename”).
-La clave privada convertida en byte[](“key”).
-La clave pública convertida en byte[](“IV”).
Será un método void (No devuelve nada).
public static void encryptToFile(String plainMessage, String filename, byte[] Key, byte[] IV){}
A continuación, crearemos el flujo del archivo a generar.
FileStream fileStream = File.Open(filename,FileMode.OpenOrCreate);
También añadiremos el flujo de la clase Rijndael.
Rijndael RijndaelAlg = Rijndael.Create();
Ahora el CryptoStream, que es el flujo de datos cifrados.
-Nota Importante-
Ten en cuenta que estamos escribiendo en un archivo, por lo que deeremos usar CryptoStreamMode.Write, más adelante veremos que con Readpodremos desencriptar dicho archivo.
CryptoStream cryptoStream = new CryptoStream(fileStream,
RijndaelAlg.CreateEncryptor(Key, IV),
CryptoStreamMode.Write);
También añadiremos el flujo de escritura StreamWriter.
StreamWriter streamWriter = new StreamWriter(cryptoStream);
Por último, ciframos el contenido del archivo y cerramos todos los flujos creados.
streamWriter.WriteLine(plainMessage);
streamWriter.Close();
cryptoStream.Close();
fileStream.Close();
El método completo quedaría así:
public static void encryptToFile(String plainMessage, String filename,
byte[] Key, byte[] IV)
{
// Crear un flujo para el archivo a generarse
FileStream fileStream = File.Open(filename, FileMode.OpenOrCreate);
// Crear una instancia del algoritmo Rijndael
Rijndael RijndaelAlg = Rijndael.Create();
// Crear un flujo de cifrado basado en el flujo de los datos
CryptoStream cryptoStream = new CryptoStream(fileStream,
RijndaelAlg.CreateEncryptor(Key, IV),
CryptoStreamMode.Write);
// Crear un flujo de escritura basado en el flujo de cifrado
StreamWriter streamWriter = new StreamWriter(cryptoStream);
// Cifrar el mensaje a través del flujo de escritura
streamWriter.WriteLine(plainMessage);
// Cerrar los flujos utilizados
streamWriter.Close();
cryptoStream.Close();
fileStream.Close();
}
Este método está listo para su uso, pero ahora vamos a crear el método de descifrado(“DecryptFromFile”), que devolverá un String.
Es muy parecido, salvo qué, en vez de escribir, vamos a tener que leer el archivo.
Esta vez solo recibirá los parámedros de:
-Nombre del archivo (“filename”).
-La clave privada convertida en byte[](“key”).
-La clave pública convertida en byte[](“IV”).
public static string decryptFromFile(String filename, byte[] Key, byte[] IV){}
Abrimos los flujos FileStream y Rijndael, exactamente igual que antes.
FileStream fileStream = File.Open(filename, FileMode.OpenOrCreate);
Rijndael RijndaelAlg = Rijndael.Create();
Añadimos el flujo de CryptoStream, salvo que esta vez con el CyptoStreamMode.Read.
CryptoStream cryptoStream = new CryptoStream(fileStream,
RijndaelAlg.CreateDecryptor(Key, IV),
CryptoStreamMode.Read);
Ahora debemos abrir el flujo StreamReader.
StreamReader streamReader = new StreamReader(cryptoStream);
Por último, al igual que antes; Deberemos desencriptar el archivo y cerrar todos los flujos usados.
Este método tiene que devolver un String, muy importante no olividar el return.
string plainMessage = streamReader.ReadLine();
streamReader.Close();
cryptoStream.Close();
fileStream.Close();
return plainMessage;
El método completo quedaría así:
public static string decryptFromFile(String filename, byte[] Key, byte[] IV)
{
// Crear un flujo para el archivo a generarse
FileStream fileStream = File.Open(filename, FileMode.OpenOrCreate);
// Crear una instancia del algoritmo Rijndael
Rijndael RijndaelAlg = Rijndael.Create();
// Crear un flujo de cifrado basado en el flujo de los datos
CryptoStream cryptoStream = new CryptoStream(fileStream,
RijndaelAlg.CreateDecryptor(Key, IV),
CryptoStreamMode.Read);
// Crear un flujo de lectura basado en el flujo de cifrado
StreamReader streamReader = new StreamReader(cryptoStream);
// Descifrar el mensaje a través del flujo de lectura
string plainMessage = streamReader.ReadLine();
// Cerrar los flujos utilizados
streamReader.Close();
cryptoStream.Close();
fileStream.Close();
return plainMessage;
}
Con esto ya hemos terminado con el archivo “Crypt”.
El Controlador
Es hora de añadir un controlador en la carpeta Controllers, el cual será el encargado de devolver las vistas y el que recibirá la información necesaria mediante el método Post.
Seleccionamos la primera opción MVC 5 Controller – Empty
En mi caso he llamado al controlador “CrypticController”.
Una vez añadido, deberemos cambiar el nombre al método Index (no es necesario) por “CifrarArchivo”.
public class CrypticController : Controller
{
// GET: CifrarArchivo
public ActionResult CifrarArchivo()
{
return View();
}
}
Añadimos el método Post, que es el que realmente va a obtener los datos de la vista.
// POST: CifrarArchivo
[HttpPost]
public ActionResult CifrarArchivo(String texto, String publica,
String privada, String archivo){}
Muy importante que tenga el mismo nombre que el Get.
Los parámetros que contiene serán <input> de la vista cuyo name serán los mismos que los que estamos viendo.
Creamos una carpeta al mismo nivel que el proyecto llamada “Files”.
En el método Post de “CifrarAchivo”, añadiremos el siguiente código para mapear la carpeta anteriormente creada.
String rute = Server.MapPath("~/Files");
String path = Path.Combine(rute, archivo + ".txt");
Necesitaremos incluir los siguiente using:
using EjemploCifrarDescifrarArchivo.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
Invocamos el método “EncryptToFile” generado en la case “Crypt”.
Hay que tener en cuenta que las claves las estamos recogiendo en formato String, por lo que habrá que usar los métodos creados con anterioridad para pasarlos a byte[].
Tiene que quedar así:
// POST: CifrarArchivo
[HttpPost]
public ActionResult CifrarArchivo(String texto, String publica,
String privada, String archivo)
{
//Mapea la carpeta especificada.
String rute = Server.MapPath("~/Files");
//Juntamos la carpeta mapeada + el nombre del archivo +
//.txt
String path = Path.Combine(rute, archivo + ".txt");
//Guardamos la clave Pública en una sesión para no tener
//que volver a preguntarla.
Session["Publica"] = publica;
//Llamamos a los metodos de Crypt para poder realizar el
//cifrado del archivo.
Crypt.encryptToFile(texto, path,
Crypt.EncodingPrivateKey(privada),
Crypt.EncodingPublicKey(publica));
//Redireccionamos a la View que permite descifrar el
//archivo.
return RedirectToAction("DescifrarArchivo", "Cryptic");
}
-Nota Importante-
En este ejemplo estoy usando Session para guardar la clave pública, pero esta clave debería ir en una base de datos asociada a un nombre de archivo, para que la clave no se pierda y siempre se pueda consultar, pero para no dificultar el ejemplo, he decidido utilizar Session.
Por lo tanto, si cierra el navegado cuando esté ejecutando el programa, el archivo al no tener una clave pública ejecutará una excepción.
También se han omitido las validaciones para poder centrar este Tutorial a su finalidad final: Cifrar y descifrar un archivo con Asp.Net MVC.
La Vista (Cifrar)
Añadimos la View de la siguiente forma. (click derecho sobre el nombre del método)
Dejamos todo por defecto.
Una vez Creada la vista introduciremos el siguiente código.
@{
ViewBag.Title = "Cifrar";
}
<h2>Crear y Cifrar un Archivo</h2>
@using (Html.BeginForm("CifrarArchivo", "Cryptic", FormMethod.Post))
{
<div class="form-group">
<label class="control-label">Texto a cifrar</label>
<textarea name="texto" class="form-control"></textarea>
</div>
<div class="form-group">
<label class="control-label">Clave Pública</label>
<input type="password" name="publica" class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Clave Privada</label>
<input type="password" name="privada" class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Nombre Arhivo</label>
<input type="text" name="archivo" class="form-control" />
</div>
<div class="form-group">
<button type="submit">Cifrar</button>
</div>
}
Creamos los métodos Get y Post de “DescifrarArchivo”, muy parecidos a los métodos anteriores.
// GET: DescifrarArchivo
public ActionResult DescifrarArchivo()
{
return View();
}
// POST: DescifrarArchivo
[HttpPost]
public ActionResult DescifrarArchivo(String privada, String archivo)
{
String rute = Server.MapPath("~/Files");
String path = Path.Combine(rute, archivo + ".txt");
ViewBag.Text = Crypt.decryptFromFile(path,
Crypt.EncodingPrivateKey(privada),
Crypt.EncodingPublicKey(Session["Publica"].ToString()));
return View();
}
Esta vez solo recibirá los parámetros del nombre del archivo a descifrar y su clave privada.
Como el método “DecryptFromFile” devuelve un String, podemos guardarlo en un ViewBag para mostrar la información en un <textarea> por ejemplo.
(Recuerda que la clave pública está almacenada en Session.)
La Vista (Descifrar)
Añadimos la vista como antes.
Incluimos el HTML en la vista que hemos creado.
@{
ViewBag.Title = "DescifrarArchivo";
}
<h2>DescifrarArchivo</h2>
@using (Html.BeginForm("DescifrarArchivo", "Cryptic", FormMethod.Post, new { @enctype = "multipart/form-data " }))
{
<div class="form-group">
<label class="control-label">Clave Privada</label>
<input type="password" name="privada" class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Nombre Arhivo</label>
<input type="text" name="archivo" class="form-control" />
</div>
<div class="form-group">
<button type="submit">DesCifrar</button>
</div>
}
<div class="form-group">
<label class="control-label">Texto Descifrado</label>
<textarea name="texto" class="formcontrol">@ViewBag.Text</textarea>
</div>
Con esto abríamos terminado todo lo necesario para el correcto funcionamiento del programa.
Puntos Importantes
Os dejo una lista de los puntos importantes que hay que tener en cuenta a la hora de querer desarrollar correctamente la aplicación:
- Entender el funcionamiento de la clase Rijndael.
- Hay que controlar las excepciones, ya que si te equivocas en la clave privada a la hora de intentar desencriptar el archivo, Rijndael lanzará una excepción .Esto se puede solucionar añadiendo un TryCatch() en el momento en el que se llama a la clase.
- Si cerramos el navegador perdemos la clave pública. Esto se puede solucionar guardando la clave en una base de datos, ya que es la pública y está diseñada para ello.
Les dejo la dirección de donde está subido el proyecto para que puedan probarlo.
También les dejo un vídeo complementario en el que les muestro el funcionamiento.
No olviden dejar algún comentario si tienen cualquier tipo de dudas.
Muchas gracias por seguir este tutorial.
Autor/a: Daniel Pizarro Cuervo
Curso: Microsoft MCSA Web Applications + Microsoft MCSD App Builder + Xamarin
Centro: Tajamar
Año académico: 2018-2019
Enlace del Proyecto: Github.
Enlace del Video: Video
Tengo una pregunta. Si el archivo no contiene texto, también funciona?.
Deseo poder encriptar archivos del tipo imagen.
Muchas Gracias