|
G A C E T A D E L I N U X
...haciendo a Linux un poco más divertido! |
|
Encriptación empleando librerías criptográficas de OpenSSL
Por Vinayak Hegde Traducción al español por Jesus Marcos
|
Linux ya ha realizado algunas incursiones en el mundo corporativo. Una de las demandas más persistentes de este mundo ha sido la necesidad de una mejor seguridad de los datos. Aquí es donde llega la encriptación, para ocultar los datos sensibles de un intruso externo. El software de código abierto tiene cierta reputación para la programación segura. Este artículo da otro paso en la misma dirección.
libcrypto de OpenSSL es una buena librería si quiere usar encriptación sin preocupaciones con los detalles de la implementación del algoritmo. El problema es que la documentación es realmente mínima. Naturalmente puede leer los fuentes y figurarse qué hacen. También el echo de que los nombres de las funciones son intuitivos ayuda hasta cierto punto. Otra forma de obtener ayuda es suscribirse a las listas de correo del sitio de OpenSSL . De cualquier manera las herramientas de la línea de comandos de OpenSSL están muy bien documentadas y sin fáciles de usar. Explicaré en este artículo como emplear el algoritmo para encriptación Bluefish usando las librrías de criptografía de OpenSSL.
Durante los primeros días de la criptografía, los algoritmos, además de las claves, eran secretos. Sin embargo actualmente esta tendencia ha cambiado. Ahora los algoritmos son publicamente conocidos y las claves son secretas. El mejor ejemplo es el algoritmo RSA que es ampliamente conocido e implementado. La clave pública es conocida para el mundo pero la clave privada es almacenada en secreto. RSA es un algoritmo asimétrico ya que no usa la misma clave para la encriptación y para la desencriptación. Aunque generalmente no es conveniente emplear RSA para encriptar grandes cntidades de datos ya que es computacionalmente caro.
Para encriptar una gran cantidad de datos, es preferible emplear algoritmos con bajo coste computacional. En este artículo empleamos el algoritmo blowfish para encriptar y desencriptar datos. Blowfish es un algoritmo asimétrico, es decir, emplea la misma clave para la encriptación y la desencriptación. Blowfish fue diseñado por el famoso criptógrafo Bruce Schneier. Blowfish es un algoritmo rápido para la encriptación/desencriptación.
Para los propositos de la demostración vamos a usar una clave de 128 bits. Esta está almacenada como un array de caracteres en el programa. También generaremos un vector de inicialización (VI) de 64 bits. Para nuestro programa usaremos el modo Cipher Block Chaining (CBC) . Además no emplearemos las funciones de blowfish directamente sino que las usaremos a través de la interfaz de alto nivel.
Un vector de inicialización es un poco de información aleatoria que es usada como entrada en los algoritmos de encriptación encadenada, esto es, cuando cada etapa de encriptación de un bloque de datos de entrada provee algún dato a la encriptación del siguiente bloque. (Blowfish emplea bloques de 64 bits para la encriptacion). El VI proporciona el primer bit de entrada para la encriptación del primer bloque, que proporciona la entrada para el segundo bloque, y así consecutivamente. El bit más a la izquierda es descartado.
Los bits aleatorios son generados a partir fichero de caracteres especiales /dev/random el cual es una buena fuente para números aleatorios. Vea la página de manual para más información.
int
generate_key ()
{
int i, j, fd;
if ((fd = open ("/dev/random", O_RDONLY)) == -1)
perror ("open error");
if ((read (fd, key, 16)) == -1)
perror ("read key error");
if ((read (fd, iv, 8)) == -1)
perror ("read iv error");
printf("128 bit key:\n");
for (i = 0; i < 16; i++)
printf ("%d \t", key[i]);
printf ("\n ------ \n");
printf("Initialization vector\n");
for (i = 0; i < 8; i++)
printf ("%d \t", iv[i]);
printf ("\n ------ \n");
close (fd);
return 0;
}
|
La rutina de encriptación obtiene dos parámetros - los descriptores de fichero del fichero de entrada y el fichero de salida en el que se van a salvar los datos encriptados. Siempre es una buena idea rellenar los buffers con ceros usando los comandos memset o bzero antes de emplear los buffers con datos. Esto es especialmente importante si tiene planeado reusar los buffers. en el programa de abajo, los datos de entrada están siendo encriptados en bloques de 1K cada uno:
Los pasos para la encriptación son los siguientes :-
Puede que se pregunte qué son los datos sobrantes. Como se mencionó hace un momento, Blowfish encripta la información en bloques de 64 bits cada uno. Algunas veces no disponemos de 64 bits para construir un bloque. Esto puede ocurrir si el tamaño del buffer en el programa de abajo o el tamaño de los datos del fichero de entrada no son un múltiplo entero de 8 bytes (64 bits). Así que por lo tanto los datos se rellenan y luego el bloque parcial es encriptado usando EVP_EncryptFinal. La longitud del bloque de datos encriptados es almacenada en la variable tlen y sumada a la longitud final.
int
encrypt (int infd, int outfd)
{
unsigned char outbuf[OP_SIZE];
int olen, tlen, n;
char inbuff[IP_SIZE];
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (& ctx);
EVP_EncryptInit (& ctx, EVP_bf_cbc (), key, iv);
for (;;)
{
bzero (& inbuff, IP_SIZE);
if ((n = read (infd, inbuff, IP_SIZE)) == -1)
{
perror ("read error");
break;
}
else if (n == 0)
break;
if (EVP_EncryptUpdate (& ctx, outbuf, & olen, inbuff, n) != 1)
{
printf ("error in encrypt update\n");
return 0;
}
if (EVP_EncryptFinal (& ctx, outbuf + olen, & tlen) != 1)
{
printf ("error in encrypt final\n");
return 0;
}
olen += tlen;
if ((n = write (outfd, outbuf, olen)) == -1)
perror ("write error");
}
EVP_CIPHER_CTX_cleanup (& ctx);
return 1;
}
|
La rutina de desencriptación básicamente sigue los mismos pasos de la rutina de desencriptación. El siguiente código muestra como se realiza la desencriptación.
int
decrypt (int infd, int outfd)
{
unsigned char outbuf[IP_SIZE];
int olen, tlen, n;
char inbuff[OP_SIZE];
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init (& ctx);
EVP_DecryptInit (& ctx, EVP_bf_cbc (), key, iv);
for (;;)
{
bzero (& inbuff, OP_SIZE);
if ((n = read (infd, inbuff, OP_SIZE)) == -1)
{
perror ("read error");
break;
}
else if (n == 0)
break;
bzero (& outbuf, IP_SIZE);
if (EVP_DecryptUpdate (& ctx, outbuf, & olen, inbuff, n) != 1)
{
printf ("error in decrypt update\n");
return 0;
}
if (EVP_DecryptFinal (& ctx, outbuf + olen, & tlen) != 1)
{
printf ("error in decrypt final\n");
return 0;
}
olen += tlen;
if ((n = write (outfd, outbuf, olen)) == -1)
perror ("write error");
}
EVP_CIPHER_CTX_cleanup (& ctx);
return 1;
}
|
Un pequeño programa interactivo implementando las rutinas de arriba puede ser descargado de aquí. El comando para compilar el programa es
# gcc -o blowfish sym_funcs.c -lcrypto |
Considere un programa de mensajería instantánea (IM) el cual quiere comunicarse con otro IM de forma segura. La siguiente aproximación podría ser seguida.
Mi vida cambió desde que descubrí Linux. De repente las computadoras se volvieron interesantes
ya que podía intentar montones de cosas en mi máquina Linux debido a la secilla disponibilidad de
los fuentes del código. Mis intereses son predominantemente en el área de red, sistemas embebidos y
lenguajes de programación. Actualmente trabajo para Aparna Web services donde hacemos Linux accesible
para academias/corporaciones configurando estaciones de arranque remoto (Thin Clients)