viernes, 31 de octubre de 2014

Repositorio de claves RSA y firmas digitales


Antes que nada, recomendaría leer mi entrada anterior acerca de RSA para entender un poco de cómo funciona esto.

Un repositorio, de acuerdo a la Real Academia de la Lengua [1], es un "Lugar donde se guarda algo." Siguiendo esa descripción, un repositorio de claves o llaves criptográficas se puede definir como un lugar donde se guardan claves para ser consultadas en cualquier momento.

En esta entrada describiré una aplicación desarrollada en Python que actúa como un repositorio casero de claves RSA, con el que no sólo se puede consultar las claves registradas en un servidor, sino que también proporciona una interfaz para cifrar y descifrar archivos de texto.

Enlace a GitHub de la aplicación.
Los componentes principales de este repositorio son:

  1. Un generador de llaves RSA, las cuales se registran en un archivo del servidor.
  2. Un mecanismo para guardar llaves RSA, tanto en el servidor como en un directorio local.
  3. Un mecanismo de consulta de llaves RSA, ya sea en los archivos del servidor o en un archivo que se encuentra en un directorio local
  4. Un mecanismo para cifrar y descifrar palabras.
  5. Un mecanismo para firmar y confirmar firmas.
  6. Un mecanismo para guardar un texto cifrado en un archivo de texto especial.
  7. Un mecanismo de autenticación tanto del servidor como del cliente
Archivos importantes de la aplicación
  • cliente.py -> ejecutable del cliente
  • sakura.py -> ejecutable del servidor
  • manipular_archivos_rsa.py -> funciones para manipular archivos
  • transformar.py -> funciones para preparar las cadenas de texto, cifrar, descifrar, firmar, etcétera

Mecanismo de registro

Las funciones principales para registrar o actualizar claves RSA en el archivo del servidor se encuentra en manipular_archivos_rsa.py y sakura.py.

Si por alguna razón las llaves no existen y se quiere consultar las llaves de otra persona, entonces primero se crean las llaves del cliente, luego se consulta los datos del destinatario y continúa el procedimiento normal de la aplicación.

Una de las mejoras que me parece muy evidente de añadir es de la verificación y posible creación de directorios, ya que si tomé en cuenta si existía los archivos utilizados por las aplicaciones, pero no acerca de la existencia de los directorios, lo cual me parece una mala idea.


Pantalla - Registro de usuarios


Solicitar datos

En cliente.py se encuentra una función que busca los datos de la persona que deseamos conocer su llave pública. Primero se busca en el directorio local (si es que existe el archivo) y luego busca en el servidor si no lo encuentra. En caso de que se decida guardar la consulta en un archivo local, se decide crear o actualizar los datos en un archivo local con los resultados del servidor.


Pantalla - Mostrar datos del destinatario
Autenticación

El mecanismo de autenticación pertenece tanto a cliente.py como a sakura.py, donde primero el cliente desafía al servidor, luego el cliente decide retar al servidor. Por cuestiones de falta de tiempo,  creo que es posible que se necesite realizar más análisis en el manejo de las llaves [4], para evitar que un mensaje firmado sobrepase a módulo n del otro involucrado. También se necesita implementar otra manera en la que el servidor obtiene su clave RSA, por defecto está en el código de esa manera pero no es algo que personalmente me guste.

También es necesario mejorar el flujo de comunicación en este mecanismo, pues si algo falla en la comunicación se rompe el flujo normal de ambos programas. Por ejemplo, si las claves del usuario son corrompidas en su directorio local, la autenticación del servidor será un falso negativo, aún si él tenga la clave pública correcta del cliente. En ese caso, mi programa cliente se cierra y no responde al servidor si lo autentica o no (sea el verdadero o un impostor), lo que rompe el flujo normal del programa servidor quien espera que el cliente acepte su reto enviado.

En ese caso admito que es un mal diseño del algoritmo y me gustaría mejorarlo para futuras implementaciones de comunicación entre cliente y servidor.


Cifras y firmas

En transformar.py se proporciona una interfaz para transformar una palabra (texto plano) o un bloque de palabras (texto cifrado) para convertirlos en lo que se desea. En mi caso decidí que los bloques de caracteres sean máximo de 3 elementos.

La función principal de este archivo es def transformar_mensaje(contenido, clave, clave_otro, cifrar): donde contenido es lo que se desea transformar, clave es la clave del cliente, clave_otro es la clave del destinatario o del emisor y cifrar es una variable booleana que indica si se va a cifrar o no el contenido.

En mi programa se firma todos los bloques, cuando en la práctica [2] es mejor firmar pocos bloques para evitar comprometer la seguridad del texto cifrado y también para evitar una sobrecarga de procesamiento innecesario, pues el algoritmo de cifrado de RSA es muchísimo más lento [5] que otros algoritmos, en especial se si compara con algoritmos de cifrado de  llaves simétricas.

En general, para cifrar una palabra sigue la siguiente secuencia:

  • La palabra se convierte en bloques.
  • Los bloques se convierten a cadenas binarias.
  • Las cadenas binarias se convierten en números.
  • Los números se firman y luego se cifran.
  • Los números se convierten en números binarios.
  • Los números binarios se convierten en bloques de caracteres.
En cambio, la secuencia para descifrar un bloque de caracteres es como sigue:
  • Los bloques de caracteres se convierten a cadenas binarias.
  • Las cadenas binarias se convierten a números.
  • Cada número se descifra y luego se verifica la firma.
  • El resultado del paso anterior se convierte en cadenas binarias.
  • Las cadenas binarias se convierten en bloques.
  • Los bloques se convierten en una palabra.


Pantalla - Cifrado de palabras
Pantalla - Descifrado de palabras

Mecanismos de creación de archivos cifrados y de descifrado de estos archivos

En cliente.py se encuentra una función que abre el archivo solicitado, si es un archivo .txt lo toma como un archivo de texto plano, cuyo contenido será cifrado y guardado en un archivo .txx. En caso de que sea un archivo .txx, el programa lo reconoce como un archivo cifrado y analiza su contenido para recuperar el mensaje original.


Pantalla - Cifrar un archivo, mostrar contenido del archivo cifrado, descifrar el archivo cifrado
Manejo de llaves

En cliente.py se encuentran funciones que realizan creación o búsquedas de llaves RSA.


Otras pantallas

Creación de archivos de consulta en directorio local



Mensajes del servidor

Tiempo que tarda en crear llaves
Tiempo que tarda en cifrar y descifrar un archivo de texto (un texto pequeño, un párrafo y el libro completo de Crimen y Castigo en inglés [3], de Fiodor Dostoievski)

En Dropbox subí el texto descifrado del libro :)
Comparación de tiempos de ejecución de cifrado y descifrado


Contenido del texto plano Número de caracteres del texto plano Tiempo para cifrar Número de caracteres generados en el texto cifrado Tiempo para descifrar
Una oración 35 caracteres <1 segundo 150 caracteres <1 segundo
Un párrafo 917caracteres <1 segundo 3504 caracteres <1 segundo
Un libro completo 1151592 caracteres 54 segundo 4476891 caracteres 52 segundos


Referencias

[1] Real Academia Española
[2] Conceptos e ideas recopiladas en las clases Seguridad de la Información y Criptografía de la doctora Elisa Schaeffer
[3] Ebook del libro de Crimen y Castigo
[4] Menezes et. Al (1996), Digital Signatures. In: "Handbook of Applied Cryptography". 435
[5] Bruce Schneier (1996), Public-Key Algorithms. In: "Applied Cryptography".

Páginas de consulta

2 comentarios:

  1. + subscription
    + query
    + one-way auth.
    + two-way auth.
    + encryption
    + decryption
    + signed msgs
    + automated key mgmt
    + client-side cache
    + performance analysis
    + use cases / example runs

    Tienes tus 10 pts completos (más no se da) ;)

    ResponderEliminar
    Respuestas
    1. Muchas gracias profesora, la veo en clases para consultar algunas dudas del case study.

      Eliminar