Tag Archives: cifrado

Cifrado y descifrado asimétrico con RSA utilizando C#/Mono

Introducción.

En criptografía, RSA (Rivest, Shamir y Adleman) es un sistema criptográfico de clave pública desarrollado en 1977. En la actualidad, RSA es el primer y más utilizado algoritmo de este tipo y es válido tanto para cifrar como para firmar digitalmente.

La seguridad de este algoritmo radica en el problema de la factorización de números enteros. Los mensajes enviados se representan mediante números, y el funcionamiento se basa en el producto, conocido, de dos números primos grandes elegidos al azar y mantenidos en secreto. Actualmente estos primos son del orden de 10200, y se prevé que su tamaño aumente con el aumento de la capacidad de cálculo de los ordenadores.

Como en todo sistema de clave pública, cada usuario posee dos claves de cifrado: una pública y otra privada. Cuando se quiere enviar un mensaje, el emisor busca la clave pública del receptor, cifra su mensaje con esa clave, y una vez que el mensaje cifrado llega al receptor, este se ocupa de descifrarlo usando su clave privada.

Se cree que RSA será seguro mientras no se conozcan formas rápidas de descomponer un número grande en producto de primos. La computación cuántica podría proveer de una solución a este problema de factorización.

Tomado del artículo RSA de Wikipedia.

Implementación.

La aplicación de demostración de esta técnica requiere del uso de por lo menos los siguientes namespaces.

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

Proveedor del servicio de cifrado RSA.

El núcleo de la implementación es el objeto RSACryptoServiceProvider que realiza las labores de cifrado/descifrado de información.

public static RSACryptoServiceProvider rsa;

Establecer los valores iniciales del servicio.

En este punto se establecen los valores iniciales de configuración del servicio, los cuales son en su mayoría opcionales, y se instancia el objeto mencionado anteriormente.

const string CONTAINER_NAME = "ContenedorRSA";
CspParameters cspParams;
cspParams = new CspParameters(1);	// PROV_RSA_FULL
cspParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
cspParams.KeyContainerName = CONTAINER_NAME;
// Instanciar el algoritmo de cifrado RSA
rsa = new RSACryptoServiceProvider(cspParams);

Generar las llaves.

En esta etapa se crea una pareja de llaves: pública y privada, para las labores de cifrado.  Estas se almacenan por conveniencia en formato XML en los archivos llave_publica.xml y llave_privada.xml correspondientemente.

// Generar y almacenar la llave pública
writer = new StreamWriter(@"llave_publica.xml");
string publicOnlyKeyXML = rsa.ToXmlString(false);
writer.Write(publicOnlyKeyXML);
writer.Close();
// Generar y almacenar la llave privada
writer = new StreamWriter(@"llave_privada.xml");
string publicPrivateKeyXML = rsa.ToXmlString(true);
writer.Write(publicPrivateKeyXML);
writer.Close();

Cargar las llaves.

Una vez que han sido creadas y almacenadas las llaves, estas no deben ser creadas nuevamente.  Para su uso se cargan desde los correspondientes archivos XML y se asocian con el proveedor de cifrado.

// Cargar y asociar la llave pública al proveedor de cifrado
StreamReader reader = new StreamReader(@"llave_publica.xml");
string publicOnlyKeyXML = reader.ReadToEnd();
rsa.FromXmlString(publicOnlyKeyXML);
reader.Close();
// Cargar y asociar la llave privada (y pública) al proveedor de cifrado
StreamReader reader = new StreamReader(@"llave_privada.xml");
string publicPrivateKeyXML = reader.ReadToEnd();
rsa.FromXmlString(publicPrivateKeyXML);
reader.Close();

Cifrar la información.

Para realizar el cifrado de los datos es necesario contar con la llave pública del destinatario.

cargarLlavePublica();
// Convertir el texto a cifrar (plano) a su representación en bytse
byte[] textoPlanoBytes = System.Text.Encoding.UTF8.GetBytes(textoPlano);
// Realizar el proceso de cifrado
byte[] textoCifradoBytes = rsa.Encrypt(textoPlanoBytes, false);
// Convertir el mensaje cifrado a su representación en cadena
string MensajeCifrado = Convert.ToBase64String(textoCifradoBytes);

Descifrar la información.

Para descifrar los datos y obtener el mensaje original es necesario contar con la llave privada del destinatario del mensaje.

cargarLlavePrivada();
// Convertir el texto cifrado a su representación en bytse
byte[] textoCifradoBytes = Convert.FromBase64String(textoCifrado);
// Realizar el proceso de descifrado
byte[] textoPlanoBytes = rsa.Decrypt(textoCifradoBytes, false);
// Convertir el mensaje descifrado a su representación en cadena
string MensajeDescifrado = System.Text.Encoding.UTF8.GetString(textoPlanoBytes);

Aplicación de demostración.

Aplicación de demostración del uso del algoritmo

Aplicación de demostración del uso del algoritmo RSA

$ gmcs "/out:RSASample.exe" "/r:/usr/lib/mono/2.0/System.dll" /t:exe "RSASample/Main.cs"

Enlaces.

Cifrado y descifrado simétrico con Rijndael (AES) utilizando C#/Mono

Introducción.

Advanced Encryption Standard (AES), también conocido como Rijndael (pronunciado "Rain Doll" en inglés), es un esquema de cifrado por bloques adoptado como un estándar de cifrado por el gobierno de los Estados Unidos. El AES fue anunciado por el Instituto Nacional de Estándares y Tecnología (NIST) como FIPS PUB 197 de los Estados Unidos (FIPS 197) el 26 de noviembre de 2001 después de un proceso de estandarización que duró 5 años.  Se transformó en un estándar efectivo el 26 de mayo de 2002. Desde 2006, el AES es uno de los algoritmos más populares usados en criptografía simétrica.

El cifrador fue desarrollado por dos criptólogos belgas, Joan Daemen y Vincent Rijmen, ambos estudiantes de la Katholieke Universiteit Leuven, y enviado al proceso de selección AES bajo el nombre "Rijndael".

Tomado del artículo Advanced Encryption Standard de Wikipedia.

Implementación con strings.

La aplicación de demostración de esta técnica requiere del uso de por lo menos los siguientes namespaces.

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

Establecer la clave y el vector de inicio.

Estos valores pueden ser especificados manualmente o de manera automática por el framework.  La implementación para permitir que se definan automáticamente estos valores es la siguiente.

Rijndael rijndael = Rijndael.Create();
byte[] key = rijndael.Key;
byte[] iv  = rijndael.IV;

Es posible forzar la generación de nuevas claves y nuevos vectores de inicio para el algoritmo utilizando los métodos rijndael.GenerateKey() y rijndael.GenerateIV() respectivamente.

Si por el contrario se desea especificar estos valores manualmente su implementación es la siguiente siendo strKey y strIv, la clave y el vector de inicialización como cadenas de texto.

byte[] key = UTF8Encoding.UTF8.GetBytes(strKey);
byte[] iv  = UTF8Encoding.UTF8.GetBytes(strIv);

Especificando estos valores manualmente es necesario garantizar que sus longitudes sean válidas para el algoritmo.  En este caso se utilizará una longitud de clave de 32 bits y una longitud de vector de inicio de 16 bits.

int keySize = 32;
int ivSize = 16;
Array.Resize(ref key, keySize);
Array.Resize(ref iv, ivSize);

Cifrado de cadenas de texto.

Para cifrar la información se requiere de los siguientes parámetros.

  1. Cadena de texto con los datos a cifrar.
  2. Clave.
  3. Vector de inicio.

El proceso retornará finalmente una cadena de texto con los datos cifrados.

/**
 * Cifra una cadena texto con el algoritmo de Rijndael
 *
 * @param	plainMessage	mensaje plano (sin cifrar)
 * @param	Key		        clave del cifrado para Rijndael
 * @param	IV		        vector de inicio para Rijndael
 * @return	string		        texto cifrado
 */
public static string encryptString(String plainMessage, byte[] Key, byte[] IV)
{
    // Crear una instancia del algoritmo de Rijndael
    Rijndael RijndaelAlg = Rijndael.Create();
    // Establecer un flujo en memoria para el cifrado
    MemoryStream memoryStream = new MemoryStream();
    // Crear un flujo de cifrado basado en el flujo de los datos
    CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                 RijndaelAlg.CreateEncryptor(Key, IV),
                                                 CryptoStreamMode.Write);
    // Obtener la representación en bytes de la información a cifrar
    byte[] plainMessageBytes = UTF8Encoding.UTF8.GetBytes(plainMessage);
    // Cifrar los datos enviándolos al flujo de cifrado
    cryptoStream.Write(plainMessageBytes, 0, plainMessageBytes.Length);
    cryptoStream.FlushFinalBlock();
    // Obtener los datos datos cifrados como un arreglo de bytes
    byte[] cipherMessageBytes = memoryStream.ToArray();
    // Cerrar los flujos utilizados
    memoryStream.Close();
    cryptoStream.Close();
    // Retornar la representación de texto de los datos cifrados
    return Convert.ToBase64String(cipherMessageBytes);
}

Descifrado de cadenas de texto.

El proceso inverso, el de descifrado, se realiza de manera antagónica.  Para hacerlo es necesario contar con los siguientes parámetros.

  1. Cadena de texto con los datos cifrados.
  2. Clave.
  3. Vector de inicio.

El proceso retornará finalmente una cadena de texto con los datos descifrados.

/**
 * Descifra una cadena texto con el algoritmo de Rijndael
 *
 * @param	encryptedMessage	mensaje cifrado
 * @param	Key			clave del cifrado para Rijndael
 * @param	IV			vector de inicio para Rijndael
 * @return	string			texto descifrado (plano)
 */
public static string decryptString(String encryptedMessage, byte[] Key, byte[] IV)
{
    // Obtener la representación en bytes del texto cifrado
    byte[] cipherTextBytes = Convert.FromBase64String(encryptedMessage);
    // Crear un arreglo de bytes para almacenar los datos descifrados
    byte[] plainTextBytes = new byte[cipherTextBytes.Length];
    // Crear una instancia del algoritmo de Rijndael
    Rijndael RijndaelAlg = Rijndael.Create();
    // Crear un flujo en memoria con la representación de bytes de la información cifrada
    MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
    // Crear un flujo de descifrado basado en el flujo de los datos
    CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                 RijndaelAlg.CreateDecryptor(Key, IV),
                                                 CryptoStreamMode.Read);
    // Obtener los datos descifrados obteniéndolos del flujo de descifrado
    int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
    // Cerrar los flujos utilizados
    memoryStream.Close();
    cryptoStream.Close();
    // Retornar la representación de texto de los datos descifrados
    return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}

Implementación con archivos.

El cifrado y descifrado de mensajes en archivos se realiza de manera similar al expuesto anteriormente con cadenas, sin embargo varían los flujos (streams) utilizados para obtener y dirigir el flujo de la información.

Cifrado a archivos.

/**
 * Cifra una cadena texto con el algoritmo de Rijndael y lo almacena en un archivo
 *
 * @param	plainMessage	mensaje plano (sin cifrar)
 * @param	filename	        nombre del archivo donde se almacenará el mensaje cifrado
 * @param	Key		        clave del cifrado para Rijndael
 * @param	IV		        vector de inicio para Rijndael
 * @return	void
 */
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();
}

Descifrado de archivos.

/**
 * Descifra el contenido de un archivo con el algoritmo de Rijndael y lo retorna
 * como una cadena de texto plano
 *
 * @param	filename		nombre del archivo donde se encuentra el mensaje cifrado
 * @param	Key			clave del cifrado para Rijndael
 * @param	IV			vector de inicio para Rijndael
 * @return	string			mensaje descifrado (plano)
 */
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;
}

Aplicación de demostración.

La aplicación de demostración incluye los conceptos y el código expuestos en este artículo.  Con ella es posible cifrar y descifrar un mensaje que consiste en una cadena de texto arbitraria en memoria y en un archivo.

Aplicación de demostración del uso del algoritmo Rijndael

Aplicación de demostración del uso del algoritmo Rijndael

Construír la aplicación.

La aplicación de demostración puede construírse utilizando la solución incluída en la distribución con MonoDevelop o Visual Studio.  También es posible construírla desde línea de comando (Mono) mediante la siguiente instrucción.

$ gmcs "/out:RijndaelSample.exe" "/r:/usr/lib/mono/2.0/System.dll" /t:exe "RijndaelSample/Main.cs"

Enlaces.