Cifrado y firmado de archivos con GPG desde linea de comando

Generar un nuevo par de llaves

$ gpg --gen-key

gpg (GnuPG) 1.4.18; Copyright (C) 2014 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:

(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)

Your selection? 1

RSA keys may be between 1024 and 4096 bits long.

What keysize do you want? (2048) 2048

Requested keysize is 2048 bits

Please specify how long the key should be valid.

0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years

Key is valid for? (0) 0

Key does not expire at all

Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: Jorge I. Meza
Email address: jimezam @autonoma.edu.co
Comment: PGP certificate for work

You selected this USER-ID: "Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O

You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy.

Not enough random bytes available. Please do some other work to give the OS a chance to collect more entropy! (Need 239 more bytes)

gpg: key 525E50C5 marked as ultimately trusted public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u pub 2048R/525E50C5 2015-10-15
Key fingerprint = 3792 F014 361E 93E7 355F 7FCA C153 2279 525E 50C5
uid Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co> sub 2048R/E56672A6 2015-10-15

Listar las llaves conocidas

Las llaves públicas

$ gpg --list-keys

/home/educacion/.gnupg/pubring.gpg
----------------------------------
pub 2048R/525E50C5 2015-10-15
uid Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co>
sub 2048R/E56672A6 2015-10-15

Las llaves privadas

$ gpg --list-secret-keys

/home/educacion/.gnupg/secring.gpg
----------------------------------
sec 2048R/525E50C5 2015-10-15
uid Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co>
ssb 2048R/E56672A6 2015-10-15

Exportar mi llave pública

$ gpg --armor --export -a 525E50C5 > public.key

Importar la llave pública de otro usuario

$ gpg --import public.key

Cifrar un archivo

$ gpg --recipient 525E50C5 --armor --encrypt ARCHIVO.EXT

En este caso se utilizará la llave pública identificada por el código 525E50C5 para cifrar el archivo.  Este proceso genera ARCHIVO.EXT.asc el cual corresponde a la versión cifrada de ARCHIVO.EXT y es el archivo que se podrá transmitir a través de un medio inseguro.

Descifrar un archivo

$ gpg --output ARCHIVO.EXT --decrypt ARCHIVO.EXT.asc

Firmar un archivo

$ gpg --detach-sign ARCHIVO.EXT

Se genera el archivo ARCHIVO.EXT.sig el cual corresponde a la firma del archivo ARCHIVO.EXT y se deberá transmitir junto con el archivo original.

Verificar la firma de un archivo

Se deberá contar con el archivo de la firma (.sig) y con el archivo original, así como con la llave pública de su supuesto autor.

$ gpg ARCHIVO.EXT.sig

En caso de que el documento corresponda con el expedido por el autor original se obtendrá un mensaje similar al siguiente.

gpg: assuming signed data in `ARCHIVO.EXT'
gpg: Signature made jue 15 oct 2015 12:40:56 COT using RSA key ID 525E50C5
gpg: Good signature from "Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co>"

En caso de que el documento NO corresponda con el expedido por el autor original se obtendrá un mensaje similar al siguiente.

gpg: assuming signed data in `ARCHIVO.EXT'
gpg: Signature made jue 15 oct 2015 12:40:56 COT using RSA key ID 525E50C5
gpg: BAD signature from "Jorge I. Meza (PGP certificate for work) <jimezam @autonoma.edu.co>"

Recursos

  1. The GNU Privacy Guard
    https://gnupg.org/
  2. GNU Privacy Guard en Wikipedia
    https://en.wikipedia.org/wiki/GNU_Privacy_Guard
  3. Curso sencillo de PGP por Arturo Quirantes Sierra
    http://www.ugr.es/~aquiran/cripto/cursopgp.htm
  4. GPG Tutorial
    https://futureboy.us/pgp.html
  5. GnuPrivacyGuardHowto en Ubuntu Documentation
    https://help.ubuntu.com/community/GnuPrivacyGuardHowto
  6. PGP Tutorial
    http://aperiodic.net/phil/pgp/tutorial.html

Construír e instalar Firesheep en GNU/Linux Ubuntu 10.10

Introducción.

Firesheep es un plugin de Firefox que permite fácilmente secuestrar sesiones HTTP en ciertas condiciones y de ciertas aplicaciones web gracias a que estas utilizan el protocolo seguro de transporte HTTPS únicamente durante la autenticación, transmitiendo el resto de la información plana permitiendo que otros usuarios se apoderen de la información de la sesión de usuario y puedan acceder a sitios como Facebook, Twitter y blogs de WordPress entre otros con las credenciales del usuario afectado.  Esto es notoriamente grave en las inalámbricas públicas que pueden ser accedidas sin autenticación alguna.

La solución a esta debilidad de las aplicaciones web será entonces utilizar el protocolo HTTPS durante toda la sesión del usuario, no sólamente durante la autenticación como estas aplicaciones afectadas hacen actualmente.  Como protección desde el lado del usuario es posible utilizar plugins como HTTPS Everywhere que obligan al navegador a utilizar el protocolo HTTPS todo el tiempo que se acceda a determinados sitios web.  De igual manera han aparecido aplicaciones que prometen detectar y combatir el uso de Firesheep en las redes y con las que estaré experimentando próximamente.

Actualmente este plugin puede descargarse para Firefox bajo Windows y OSX, la versión para Linux no se encuentra actualmente disponible, sin embargo como el proyecto es de código abierto es muy fácil obtener el código y compilarlo para esta plataforma tal y como se describe a continuación.

Construír el software.

Instalar git.

El cliente de git es necesario para acceder al código fuente del plugin que se encuentra almacenado en un repositorio de GitHub.

$ sudo aptitude install git-core

Obtener el código fuente.

$ mkdir /tmp/firesheep && cd /tmp/firesheep

$ git clone https://github.com/codebutler/firesheep.git

$ cd firesheep

$ git submodule update --init

Instalar las dependencias.

Estos paquetes son necesarios para la construcción del plugin a partir de su código fuente.

$ sudo apt-get install autoconf libtool libpcap-dev libboost-all-dev libhal-dev xulrunner-1.9.2-dev

Construír el plugin.

$ ./autogen.sh

$ make

Instalar el software.

Desde Firefox elija la opción de abrir un archivo (CTRL+O).

File > Open File…

Y seleccione el archivo /tmp/firesheep/firesheep/build/firesheep.xpi.

Usar el software.

Una vez activado el plugin este puede visualizarse como una barra lateral mediante View > Firesheep o la combinación de teclas CTRL+SHIFT+S.

Firesheep en Ubuntu 10.10
Firesheep en Ubuntu 10.10

Su uso es muy sencillo, simplemente presione el botón Start capturing y espere a que la aplicación capture la información de las sesiones en la red, las cuales aparecerán en la barra lateral situada al lado izquierdo.

Enlaces.

Instalar un firewall personal en GNU/Linux Ubuntu 10.10

Introducción.

A pesar de las apariencias iniciales, Ubuntu si incluye un firewall sólo que este no incluye reglas ya que por defecto no vienen servicios abiertos.  El firewall incluído es Netfilter y para él se incluye además una herramienta que facilita su administración llamada Uncomplicated Firewall (ufw).

Esta herramienta es muy sencilla de usar, sin embargo es de línea de comando y esto en algunas ocasiones la hace mas compleja de utilizar.  Para esto se incluye en los repositorios de Ubuntu a gufw el cual es un front-end grafico para ufw.

Instalación y ejecución de gufw.

$ sudo aptitude install gufw

Para ejecutarlo acceda a la aplicación a través de los siguientes menúes.

System > Administration > Firewall Configuration.

Funcionalidades de gufw.

En la ventana inicial de la aplicación es posible activarlo y desactivarlo, definir las políticas generales de entrada y salida, y las reglas o excepciones específicas.

Ventanan inicial de gufw
Ventanan inicial de gufw

Generalmente la política utilizada es nada entra-todo sale, es decir, se confía en el uso que las aplicaciones de la máquina va a hacer de la red.  Una aproximación mas segura sería la mas restrictiva de nada entra-nada sale pero sería necesario especificar uno a uno los sitios a los cuales se realizará conexión (incluyendo los sitios web!) así como efectivamente será necesario hacerlo para las conexiones entrantes.

Las reglas pueden crearse utilizando tres aproximaciones que van desde la mas simple en la que se eligen aplicaciones o servicios predefinidos, pasando por una intermedia en la que es posible especificar puertos y o eligiendo una opción avanzada con la cual no solo es posible elegir puertos sino que también es posible establecer rangos de direcciones IP.

Creación de reglas con gufw
Creación de reglas con gufw

También es posible verificar el registro del servicio del cual puede determinarse su nivel de profundidad.

Registro de eventos con gufw
Registro de eventos con gufw

Conclusión.

Existen otras soluciones de firewall para Linux mas elaboradas que ufw (como por ejemplo Zentyal, antes ebox), sin embargo su combinación con gufw es suficiente para las necesidades de filtrado de la mayoría de los escritorios.

Enlaces.

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.

Calculando hashes (MD5 y SHA1) con C#/Mono

Introducción.

Una función de hashing recibe un bloque de datos arbitrarios y genera de manera determinística un arreglo de bytes con el valor de su hash, el cual tiene como característica el ser único para los datos procesados.

Los valores de hash tienen múltiples usos, entre ellos se utilizan en la criptografía para garantizar que la información de un mensaje no ha sido modificada.  Con este mismo uso, comúnmente se utiliza para determinar si la descarga de archivos ha sido exitosa o no (md5sum).

Existen varios algortimos de hashing, los mas conocidos son MD5 de 128 bits y SHA1 de 160 bits.  El primero de ellos es muy utilizado actualmente, especialmente para el almacenamiento de contraseñas, sin embargo ya no se considera seguro por lo cual es conveniente utilizar el segundo mencionado.

Implementación en C#/Mono.

Para la implementaciones que se describen a continuación se utilizaron los siguientes namespaces.

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

Calcular el hash de una cadena.

El primer paso es crear instancias de los algoritmos de hashing.

HashAlgorithm md5  = HashAlgorithm.Create("MD5");
HashAlgorithm sha1 = HashAlgorithm.Create("SHA1");

Posteriormente se calcula el valor del hash de la cadena texto (text) especificada.

Byte[] md5Data  = md5.ComputeHash(UTF8Encoding.UTF8.GetBytes(text));
Byte[] sha1Data = sha1.ComputeHash(UTF8Encoding.UTF8.GetBytes(text));

Finalmente su valor puede ser manipulado o entregado al usuario.

Console.WriteLine("MD5:  " + BitConverter.ToString(md5Data).Replace("-", ""));
Console.WriteLine("SHA1: " + BitConverter.ToString(sha1Data).Replace("-", ""));

Calcular el hash de un archivo.

El procedimiento es muy similar al cálculo de hash de una cadena, pero a diferencia de este, los datos provienen de un flujo de archivo al cual se le calcula su valor.

FileStream fs = File.OpenRead(filename);

md5Data  = md5.ComputeHash(fs);
sha1Data = sha1.ComputeHash(fs);

fs.Close ();

Verificar la seguridad de las contraseñas de GNU/Linux con John the Ripper

Introducción.

Una de las primeras cosas que debemos verificar quienes estamos preocupados por la seguridad de los servidores y aplicaciones es la seguridad de las contraseñas que eligen y utilizan a diario los usuarios.  A nivel de aplicación y de sistema operativo se establecen políticas que les obligan a los usuarios a elegir combinaciones de caracteres con una confiabilidad que consideramos suficiente.  Pero cuan seguras son realmente esas claves ?

John the Ripper es una aplicación que pretende averiguar las contraseñas a través de un ataque de fuerza bruta.  Para esto es necesario contar con acceso a la base de datos de los usuarios (/etc/passwd) y de las contraseñas cifradas (/etc/shadow) del sistema operativo.  Su uso es muy simple y como se mencionó anteriormente, es muy útil para establecer si las contraseñas son fáciles de establecer o no mediante este tipo de ataques.

Instalación en Ubuntu.

Simplemente es necesario instalar el paquete que se encuentra en los repositorios estándar.

$ sudo aptitude install john

Utilizar JTR.

El primer paso consiste en generar un archivo intermedio entre la lista de usuarios y sus contraseñas cifradas.

$ sudo unshadow /etc/passwd /etc/shadow > usuarios.db

A continuación se inicia el proceso de verificación de contraseñas por fuerza bruta.

$ john usuarios.db

Este proceso puede tomar una cantidad considerable de tiempo y recursos de procesamiento.  En cualquier momento es posible consultar las contraseñas que ya han sido determinadas y la cantidad de contraseñas pendientes mediante la siguiente instrucción.

$ john -show usuarios.db

Es posible continuar con una sesión previa que fue interrumpida ubicándose en el mismo directorio donde se encuentra el archivo de datos y ejecutando la siguiente instrucción.

$ john -restore

La información del proceso y las contraseñas encontradas se almacenan bajo la ruta ~/.john.

Enlaces.