anterior índice siguiente

Envelope Encryption Routines

La principal desventaja de los algortimos de clave pública es su lentitud. Para subsanar esa deficiencia se combinan con los algoritmos de clave simétrica en lo que se suele denominar envelope encryption/decryption. EVP implementa funciones que permiten tratar este aspecto de la criptografía de manera directa. Lo vemos en dos apartados:


La encriptación

Veamos (una vez más :-P) las funciones de inicialización, actualización y finalización:

  • int EVP_SealInit(EVP_CIPHER_CTX *ctx, EVP_CIPHER *type, unsigned char **ek,int *ekl, unsigned char *iv,EVP_PKEY **pubk, int npubk);
  • int EVP_SealUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, unsigned char *in, int inl);
  • int EVP_SealFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);

La función de inicialización genera una clave de sesión aleatoria para el algoritmo (type) especificado. Dicha clave de sesión se cifra con cada una de las claves públicas que le pasamos en el parámetro pubk, devolviendo el resultado en ek. Así pues ek[0] apuntará a la clave de sesión encriptada con pubk[0], ek[1] con pubk[1] y así sucesivamente. npubk es el número de claves públicas que pasamos en pubk. Veamos un ejemplo:


<!--#include file="ejemplos/sealopen/EVP_seal_e1.c" -->

Ejemplo 1. Inicializamos un contexto de encriptación.

Pese a lo que pueda parecer (ojito porque las páginas man están incorrectas) el parámetro iv es de salida y no de entrada. La función, además de generar una clave de sesión aleatoria genera también un vector de inicialización aleatorio (si procede), que almacena en la zona apuntada por iv.

Es necesario reservar espacio para cada una de las componenetes del vector ek. El tamaño en bytes necesario se puede obtener con la macro EVP_PKEY_size(EVP_CIPHER *).

Como en el caso de EVP_EncryptInit(), que vimos en la parte de cifrado simétrico, EVP_SealInit() puede llamarse repetidas veces. El motivo, el mismo: establecer algunos parámetros variables de los algoritmos (como la longitud de la clave). Para hacer esto el protocolo es el siguiente (atento porque esto no está muy bien documentado en las páginas man):

  1. Llamar a EVP_SealInit() con el parámetro npubk puesto a 0, el contexto que vamos a inicializar y el parámetro type. El resto de parámetros son ignorados.
  2. Establecer los parámetros en cuestión.
  3. Llamar a EVP_SealInit() con type puesto a NULL y el resto de parámetros con los valores correspondientes.

Veamos un ejemplo.


<!--#include file="ejemplos/sealopen/EVP_seal_e2.c" -->

Ejemplo 2. Inicializar un contexto modificando parámetros de algoritmos.

El ejemplo establece la longitud de clave del RC4 a 40 bits. Existe un pequeño problema que no viene documentado pero que viendo el código de la función se aprecia eseguida. EVP_SealInit() devuelve 0 si se produce un error y npubk si todo funciona bien. Sin embargo esto no es así siempre. Cuando npubk es igual a 0 EVP_SealInit() devuelve también cero, sin embargo esto no es necesariamente un indicador de que algo ha ido mal. El ejemplo 2 da buena fe de ello. Nuestro parámetro npubk vale cero porque queremos establecer una longitud de clave distinta. En estos casos la única forma que tenemos de detectar el error es comprobando que la cola de errores asociada al thread de ejecución está vacía. Para ello puede consultar el tutorial sobre OpenSSL/ERR. Realmente no alcanzo a comprender el porqué de ese comportamiento.

En cuanto a las funciones de actualización y finalización tienen el mismo comportamiento que sus homólogas en la parte de cifrado simétrico. Veamos un ejemplo:


<!--#include file="ejemplos/sealopen/EVP_seal_e3.c" -->

Ejemplo 3. Encriptamos una cadena de caracteres.


La desencriptación

Veamos las ya míticas funciones de inicialización, actualización y verificación.

  • int EVP_OpenInit(EVP_CIPHER_CTX *ctx,EVP_CIPHER *type,unsigned char *ek, int ekl,unsigned char *iv,EVP_PKEY *priv);
  • int EVP_OpenUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, unsigned char *in, int inl);
  • int EVP_OpenFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);

Nuevamente obsérvese la similitud entre EVP_OpenUpdate() y EVP_OpenFinal() con EVP_DecrypUpdate() y EVP_DecryptFinal(). Su funcionamiento es el mismo de modo que vamos a centrarnos en la función de inicialización.

EVP_OpenInit() toma como parámetros el contexto a inicializar (ctx), el algoritmo que se utilizará para descifrar (type), una clave encriptada con un algoritmo de clave publica (ek), la longitud de ek (ekl), el vector de inicialización (iv) y la clave privada que utilizaremos para obtener (Open) la clave simétrica que tenemos en ek.

Fácil... ¿no? Pues vamos a por otro ejemplo.


<!--#include file="ejemplos/sealopen/EVP_seal_e4.c" -->

Ejemplo 4. Inicializamos un contexto para desencriptación.

El ejemplo 4 es una ampliación del 3. Inicializamos el contexto al mismo tiempo que establecemos la longitud de clave del RC4. El protocolo para hacer esto último ahora es el siguiente:

Para acabar vamos a desencriptar el mensaje con cada una de las claves que han sido encriptadas.


<!--#include file="ejemplos/sealopen/EVP_seal_e5.c" -->

Ejemplo 5. Desencriptamos una cadena de caracteres.


anterior índice siguiente