Criptografía.
Usando las clases de la plataforma .net

Fecha: 16 de Septiembre de 2003 (18/Sep/2003)
Autor: Gonzalo Antonio Sosa M. E-mail


Clase Crypto

Esta clase hará uso de la clase escrita anteriormente creando los objetos que permitirán cifrar los flujos con la información. Así que sin más este es el código.

Importamos los namespaces necesarios.

using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;

Definimos nuestras variables y las enumeraciones de manera similar a como se hizo en la clase CryptoServiceProvider, con la diferencia que ahora los modificadores serán todos public en vez de internal, al igual que el modificador de acceso de la clase.

// Establecemos las variables privadas que contendrán
// la llave de encripción así como el vector de inicialización.
private string stringKey;
private string stringIV; 
// Acción a ejecutar dentro de los métodos.
private CryptoProvider algorithm;

/// <summary>
/// Proveedores del Servicio de criptografía.
/// </summary>
public enum CryptoProvider
{
    DES,
    TripleDES,
    RC2,
    Rijndael
}

/// <summary>
/// Encripción / Desencripción.
/// </summary>
public enum CryptoAction
{
    Encrypt,
    Desencrypt
}

El contructor por defecto, tomará como parámetro el tipo de algoritmo deseado para realizar el cifrado.

/// <summary>
/// Constructor por defecto.
/// </summary>
/// <param name="alg">Establece el algoritmo de Encripción a utilizar.</param>
public Crypto(CryptoProvider alg)
{
    this.algorithm = alg;
}

Estableceremos dos propiedades, una que establezca o devuelva el valor de la llave de encripción, y otra para el valor del vector de inicialización.

/// <summary>
/// Propiedad que obtiene o establece el valor de la llave de encripción
/// </summary>
public string Key
{
    get
    {
    return stringKey;
    }
    set
    {
        stringKey = value;
    }
}

/// <summary>
/// Propiedad que obtiene o establece el valor del vector de inicialización.
/// </summary>
public string IV 
{
    get
    {
        return stringIV;
    }
    set
    {
        stringIV = value;
    }
}

Hasta aquí no hemos entrado aún en lo que ha encriptar se refiere, antes debemos aclarar un tanto los métodos definidos a continuación.

Como ya se mencionó, de lo que se trata es de crear un clase genérica que nos permita cifrar y descifrar tanto archivos como cadenas de caracteres. Bien esta clase por tanto, debe contemplar métodos que hagan cada una de esas operaciones. Los valores de la llave y el vector de inicialización serán proporcionado por medio de propiedades escritas anteriormente. No siendo así con el proveedor (algoritmo) seleccionado, que deberá pasarse como parámetro del constructor. Hay que comprender algo al momento de trabajar con estos algoritmos de cifrado simétrico, esto es, la longitud de su llave y vector de inicialización (VI de ahora en adelante); la longitud de la llave varía de un algoritmo a otro, quedando de la siguiente forma:

Algoritmo Longitud de la clave
DES Igual a 64 bits (8 bytes)
TripleDES Entre 128 y 192 bits (16 y 24 bytes)
RC2 Entre 64 y 128 bits (8 y 16 bytes)
Rijndael Entre 128 y 256 bits (16 y 32 bytes)

Para el vector de inicialización el caso es el mismo, varía dependiendo del algoritmo del que se trate.

Algoritmo Longitud del VI
DES Igual a 64 bits (8 bytes)
TripleDES
RC2
Rijndael Igual a 128 bits (16 bytes)

Como se mencionó anteriormente los valores que reciben los métodos que generan las secuencias de encripción, deben ser por fuerza, arreglos de bytes; de modo que se han escrito dos métodos que se encargan de convertir los valores de las clave y del VI, de cadenas tipo string en sus respectivos arreglo de bytes. A su vez comprueban la longitud de la dichos valores y, en caso de ser necesario, completan o truncan su longitud hasta alcanzar valores válidos para el algoritmo en que vayan a ser utilizados. Generan el arreglo de bytes usando los métodos del sistema de codificación UTF8.

/// <summary>
/// Convierte los valores de tipo string, de la llave de cifrado
/// en sus correspondiente byte array.
/// </summary>
/// <returns>Devuelve el arreglo de bytes correspondiente a la llave de cifrado.</returns>
private byte[] MakeKeyByteArray()
{
    // dependiendo del algoritmo utilizado.
    switch (this.algorithm)
    {
        // para los algoritmos
        case CryptoProvider.DES:
        case CryptoProvider.RC2:
            // verificamos que la longitud no sea menor que 8 bytes,
            if (stringKey.Length < 8)
                // de ser así, completamos la cadena hasta un valor válido
                stringKey = stringKey.PadRight(8);
            else if (stringKey.Length > 8)// si la cadena supera los 8 bytes,
                // truncamos la cadena dejándola en 8 bytes.
                stringKey = stringKey.Substring(0, 8); 
            break;
        // para los algoritmos
        case CryptoProvider.TripleDES:
        case CryptoProvider.Rijndael:
            // verificamos que la longitud no sea menor a 16 bytes
            if (stringKey.Length < 16)
                // de ser así, completamos la cadena hasta esos 16 bytes.
                stringKey = stringKey.PadRight(16); 
            else if (stringKey.Length > 16)//longitud es mayor a 16 bytes,
            // truncamos la cadena dejándola en 16 bytes.
                stringKey = stringKey.Substring(0, 16);
            break;
    }

    // utilizando los métodos del namespace System.Text, 
    // convertimos la cadena de caracteres en un arreglo de bytes
    // mediante el método GetBytes() del sistema de codificación UTF.
    return Encoding.UTF8.GetBytes(stringKey);
}

/// <summary>
/// Convierte los valores de tipo string, del vector de inicialización
/// en sus correspondiente byte array.
/// </summary>
/// <returns>Devuelve el arreglo de bytes correspondiente al VI.</returns>
private byte[] MakeIVByteArray()
{
    // dependiendo del algoritmo utilizado.
    switch (this.algorithm)
    {
        // para los algoritmos 
        case CryptoProvider.DES:
        case CryptoProvider.RC2:
        case CryptoProvider.TripleDES:
            // verificamos que la longitud no sea menor que 8 bytes, 
            if (stringIV.Length < 8)
                // de ser así, completamos la cadena hasta un valor válido
                stringIV = stringIV.PadRight(8);
            else if (stringIV.Length > 8)//si la cadena supera los 8 bytes,
                // truncamos la cadena dejándola en 8 bytes.
                stringIV = stringIV.Substring(0, 8);
            break;
        case CryptoProvider.Rijndael:
            // verificamos que la longitud no sea menor que 16 bytes,
            if (stringIV.Length < 16)
                // de ser así, completamos la cadena hasta un valor válido
                stringIV = stringIV.PadRight(16);
            else if (stringIV.Length > 16)//si la cadena supera los 16 bytes,
                // truncamos la cadena dejándola en 16 bytes.
                stringIV = stringIV.Substring(0, 16);
            break;
}

    // utilizando los métodos del namespace System.Text, 
    // convertimos la cadena de caracteres en un arreglo de bytes
    // mediante el método GetBytes() del sistema de codificación UTF.
    return Encoding.UTF8.GetBytes(stringIV);
}

A continuación se codifican los métodos que realizan el trabajo de cifrado de cadenas de caracteres.

/// <summary>
/// Cifra la cadena usando el proveedor especificado.
/// </summary>
/// <param name="CadenaOriginal">Cadena que será cifrada.</param>
/// <returns>Devuelve la cadena cifrada.</returns>
public string CifrarCadena(string CadenaOriginal)
{ 
    // creamos el flujo tomando la memoria como respaldo.
    MemoryStream memStream = null;
    try
    {
        // verificamos que la llave y el VI han sido proporcionados.
        if (stringKey != null && stringIV != null)
        {
            // obtenemos el arreglo de bytes correspondiente a la llave
            // y al vector de inicialización.
            byte[] key = MakeKeyByteArray(); 
            byte[] IV = MakeIVByteArray(); 
            // convertimos el mensaje original en sus correspondiente
            // arreglo de bytes.
            byte[] textoPlano = Encoding.UTF8.GetBytes(CadenaOriginal);
            // creamos el flujo 
            memStream = new MemoryStream(CadenaOriginal.Length * 2);
            // obtenemos nuestro objeto cifrador, usando la clase 
            // CryptoServiceProvider codificada anteriormente.
            CryptoServiceProvider cryptoProvider = new CryptoServiceProvider((CryptoServiceProvider.CryptoProvider) this.algorithm, 
            CryptoServiceProvider.CryptoAction.Encrypt);
            ICryptoTransform transform = cryptoProvider.GetServiceProvider(key, IV);
            // creamos el flujo de cifrado, usando el objeto cifrador creado y almancenando
            // el resultado en el flujo MemoryStream.
            CryptoStream cs = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
            // ciframos el mensaje.
            cs.Write(textoPlano, 0, textoPlano.Length);
            // cerramos el flujo de cifrado.
            cs.Close();
        }
        else
            // si los valores de la llave y/o del VI no se han establecido
            // informamos mendiante una excepción.
            throw new Exception("Error al inicializar la clave y el vector");
    }
    catch 
    {
        throw; 
    }
    // la conversión se ha realizado con éxito,
    // convertimos el arreglo de bytes en cadena de caracteres, 
    // usando la clase Convert. Debido al formato UTF8 utilizado nos valemos
    // del método ToBase64String para tal fin.
    return Convert.ToBase64String(memStream.ToArray());
}

/// <summary>
/// Descifra la cadena usando al proveedor específicado.
/// </summary>
/// <param name="CadenaCifrada">Cadena con el mensaje cifrado.</param>
/// <returns>Devuelve una cadena con el mensaje original.</returns>
public string DescifrarCadena(string CadenaCifrada)
{
    // creamos el flujo tomando la memoria como respaldo.
    MemoryStream memStream = null;
    try
    {
        // verificamos que la llave y el VI han sido proporcionados.
        if (stringKey != null && stringIV != null)
        {
            // obtenemos el arreglo de bytes correspondiente a la llave
            // y al vector de inicialización.
            byte[] key = MakeKeyByteArray();
            byte[] IV = MakeIVByteArray();
            // obtenemos el arreglo de bytes de la cadena cifrada.
            byte[] textoCifrado = Convert.FromBase64String(CadenaCifrada);

            // creamos el flujo
            memStream = new MemoryStream(CadenaCifrada.Length);
            // obtenemos nuestro objeto cifrador, usando la clase 
            // CryptoServiceProvider codificada anteriormente.
            CryptoServiceProvider cryptoProvider = new CryptoServiceProvider((CryptoServiceProvider.CryptoProvider) this.algorithm, 
            CryptoServiceProvider.CryptoAction.Desencrypt);
            ICryptoTransform transform = cryptoProvider.GetServiceProvider(key, IV);
            // creamos el flujo de cifrado, usando el objeto cifrador creado y almancenando
            // el resultado en el flujo MemoryStream.
            CryptoStream cs = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
            cs.Write(textoCifrado, 0, textoCifrado.Length); // ciframos
            cs.Close(); // cerramos el flujo.
        }
        else
            // si los valores dela llave y/o del VI no se han establecido
            // informamos mendiante una excepción.
            throw new Exception("Error al inicializar la clave y el vector.");
    }
    catch 
    {
        throw;
    }

    // utilizamos el mismo sistema de codificación (UTF8) para obtener 
    // la cadena a partir del byte array.
    return Encoding.UTF8.GetString(memStream.ToArray());
}

Por último codificamos el método que nos permitirá cifrar un archivo y almacenar el resultado en otro archivo destino. Se decidió mantener toda la lógica de la codificación del cifrado y descifrado en un método, porque el funcionamiento es idéntico y la acción se define por uno de los parámetros de dicho método.

/// <summary>
/// Cifrar o Descifrar el archivo especificado y almacenada la información
/// cifrada en el archivo destino proporcionado.
/// </summary>
/// <param name="InFileName">Nombre del archivo original.</param>
/// <param name="OutFileName">Nombre del archivo destino.</param>
/// <param name="Action">Acción a realizar sobre el archivo.</param>
public void CifrarDescifrarArchivo(string InFileName, string OutFileName, CryptoAction Action)
{
    // si el archivo especificado no existe,
    if (! File.Exists(InFileName))
    {
        // generamos una excepción informándolo.
        throw new Exception("No se ha encontrado el archivo.");
    }

    try
    {
        // si la llave de cifrado y el VI están establecidos
        if (stringKey != null && stringIV != null)
        {
            // creamos el flujo de entrada, desde el archivo original.
            FileStream fsIn = new FileStream(InFileName, FileMode.Open, FileAccess.Read);
            // creamos el flujo de salida, hacía el archivo de salida especificado.
            FileStream fsOut = new FileStream(OutFileName, FileMode.OpenOrCreate, FileAccess.Write);
            // establecemos la capacidad del archivo de salida a 0.
            fsOut.SetLength(0);

            byte[] key = MakeKeyByteArray(); // creamos la llave de cifrado.
            byte[] IV = MakeIVByteArray(); // y el vector de inicialización.
            byte[] byteBuffer = new byte[4096]; // creamos un buffer de bytes.
            long largoArchivo = fsIn.Length; // establecemos variables de control
            long bytesProcesados = 0; // para la lectura y escritura de los archivos.
            int bloqueBytes = 0;
            // creamos nuestro objeto cifrador, desde la clase CryptoServiceProvider.
            CryptoServiceProvider cryptoProvider = new CryptoServiceProvider((CryptoServiceProvider.CryptoProvider)         this.algorithm,
(CryptoServiceProvider.CryptoAction) Action);
            ICryptoTransform transform = cryptoProvider.GetServiceProvider(key, IV);
            CryptoStream cryptoStream = null;

            // dependiendo de la acción especificada,
            switch (Action)
            {
                case CryptoAction.Encrypt:
                // creamos el cifrador, almacenando el resultado en el flujo de salida.
                cryptoStream = new CryptoStream(fsOut, transform, CryptoStreamMode.Write);
                break;
                // ó el descifrador, almacenando el resultado en el flujo de salida.
                case CryptoAction.Desencrypt:
                cryptoStream = new CryptoStream(fsOut, transform, CryptoStreamMode.Write);
                break;
            }

            // mientras el número de bytes procesados es menor que el
            // largo del archivo
            while (bytesProcesados < largoArchivo)
            {
                // leemos un bloque de datos y lo almacenamos en el buffer.
                bloqueBytes = fsIn.Read(byteBuffer, 0, 4096);
                // ciframos los datos del buffer
                cryptoStream.Write(byteBuffer, 0, bloqueBytes);
                // incrementamos el contador de bytes procesados.
                bytesProcesados += (long) bloqueBytes;
            }

            // cerramos los flujos de datos.
            if (cryptoStream != null)
                cryptoStream.Close();
            fsIn.Close();
            fsOut.Close();
        }
        else
            // si los valores de la llave de cifrado y/o el VI no se han
            // asignado, lo informámos mediante una excepción.
            throw new Exception("Error al inicializar la clave y el vector.");
    }
    catch 
    {
        throw;
    }
}

y eso ha sido todo...para esta clase.

Anterior Menú Siguiente

ir al índice