Ir al contenido principal

Criptografía (CCLXXVIII): Firma digital y cifrado RSA en python

Continúo poniendo scripts de programación en python para automatizar tareas que tengan relación con la criptografía.

En este post incluyo un script en python para  implementar la firma digital y el cifrado con el algoritmo RSA.

Antes de poner el script, recordar que en este post decía que el algoritmo RSA sirve tanto para cifrar y descifrar (confidencialidad) como para firmar digitalmente un texto en claro (autenticidad e integridad), y que en la práctica se usa en el cifrado híbrido para cifrar una clave de sesión (un número) que, a su vez, se utiliza como clave para cifrar el mensaje o archivo completo mediante un algoritmo de cifrado simétrico. Esto es debido a que RSA (criptografía asimétrica) es menos eficiente, es decir, es más lento y consume más recursos, que un algoritmo de los usados en criptografía simétrica.

Para hacer ambas cosas más eficientes, los esquemas modernos de cifrado utilizan conjuntamente los conceptos de clave pública y clave privada, es decir, el mensaje o archivo completo se cifra con un algoritmo de clave privada o simétrica (por ejemplo, AES) y luego la clave empleada en ese cifrado (clave de sesión), a su vez, se cifra con un algoritmo de clave pública o asimétrica (por ejemplo, RSA):

Por tanto, como se ve en la figura anterior, el texto en claro se cifra mediante una clave de sesión, un número correspondiente a cada mensaje particular, y la clave de sesión se cifra utilizando la clave pública del receptor, de tal manera que sólo el receptor puede descifrar la clave de sesión mediante su clave privada (criptografía asimétrica) y posteriormente descifrar el criptograma o texto en claro cifrado mediante la clave de sesión (criptografía simétrica).

De esta forma, sólo se podrá obtener la clave de sesión (clave simétricasi se está en posesión de la clave privada RSA, y, por consiguiente, si no se tiene esta última no se puede descifrar el criptograma o texto en claro cifrado.

Este esquema de cifrado se puede utilizar para mantener la confidencialidad de los mensajes y los archivos, sin tardar demasiado ni consumir demasiados recursos informáticos, pero, como digo, también se puede utilizar para firmar digitalmente los mensajes y los archivos, es decir, para garantizar al receptor que el emisor es realmente quien los ha enviado (autenticidad: el emisor es quien dice ser) y para comprobar que no han sido interceptados y alterados por terceros (integridad); con la característica adicional de no repudio, tanto en origen (el emisor no puede negar que creó y envió esos textos en claro) como en destino (el receptor no puede negar que los recibió y recibió exactamente esos textos en claro).

El script que pongo a continuación implementa las operaciones de firma digital del texto en claro y de cifrado de la clave de sesión mediante RSA, y el cifrado del texto en claro mediante el algoritmo AES de criptografía simétrica en modo de operación CBC, es decir, las operaciones que se realizan en la comunicación desde el lado del emisor.

Previamente he generado, mediante el script que puse en esta entrada, el par de claves RSA, publica y privada, para los dos usuarios participantes en la comunicación, emisor y receptor: clave pública emisor, clave privada emisor, clave pública receptor, clave privada receptor.

Asimismo, para que el script funcione se necesita importar el siguiente módulo en el programa principal, que se encargará de la exponenciación modular rápida a realizar en las operaciones de cifrado RSA, tanto de la firma digital como de la clave de sesión, para hacer éstas más eficientemente.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# ALGORITMO DE EXPONENCIACIÓN MODULAR RÁPIDA:
#
# Algoritmo utilizado en operaciones de exponenciación modular
# con números muy grandes con objeto de hacerlas de forma
# más eficiente.
#
# http://mikelgarcialarragan.blogspot.com/

def exp_modular_rapida(b, e, m):
    c = 1
    e = bin(e)[2:]

    for i in range(0, len(e)):
        c = pow(c, 2, m)
        if e[i]=="1":
            c = c*b%m

    return c

El script es el siguiente:

Script python para la firma digital y cifrado RSA:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# CIFRADO RSA:
#
# Firma digitalmente y cifra utilizando el criptosistema RSA.
#
# http://mikelgarcialarragan.blogspot.com/

import hashlib
from Crypto import Random
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
from base64 import b64encode
from exponenciacion_modular_rapida import exp_modular_rapida

# CIFRADO AES (CRIPTOGRAFÍA SIMÉTRICA).
def cifrar_aes(texto_claro,clave):
    iv = Random.new().read(AES.block_size)
    print("[+] IV (hexadecimal):", iv.hex())
    cifrar = AES.new(clave, AES.MODE_CBC, iv)
    criptograma = iv + cifrar.encrypt(pad(texto_claro.encode(), AES.block_size))
    return criptograma

# OBTENER DATOS DE LA CLAVE RSA.
def clave_rsa(fichero_pem):
    f_clave_rsa = open(fichero_pem, "r")
    datos_clave_rsa = RSA.importKey(f_clave_rsa.read())
    f_clave_rsa.close()
    return datos_clave_rsa

def main():
# MENÚ:
# Se presenta el menú para que se seleccione una opción.
    salir = False
    while not salir:
        print ("")
        print ("*** MENÚ *****************************************")
        print ("1. Firmar digitalmente y cifrar clave de sesión AES.")
        print ("2. Salir.")
        print ("")
        opcion = input("Por favor, seleccione una opción: ")
        if opcion == "1":
            print ("")
            print ("--- FIRMAR DIGITALMENTE Y CIFRAR CLAVE DE SESIÓN:")
            # Firmar digitalmente y cifrar: Se introducen el texto en claro y la clave.
            texto_claro = clave = ""
            while texto_claro == "":
                texto_claro = input('Texto en claro a cifrar: ')
                if texto_claro != "":
                    while clave == "":
                        clave = input('Clave: ')
                        if clave != "":
                            # Se genera la clave de sesión AES con la que se va a cifrar el
                            # texto en claro y que, a su vez, va a ser cifrada con la clave
                            # pública del destinatario, que será descifrada por éste con su
                            # clave privada (criptografía asimétrica) para, a su vez, descifrar
                            # el criptograma (criptografía simétrica).
                            # La clave se genera a partir de la clave introducida por el usuario,
                            # que puede tener cualquier longitud, y de la cual se calcula su
                            # hash SHA-256 (256 bits).
                            # Ese hash será la clave de sesión (32 bytes).
                            clave_sesion = (hashlib.sha256(clave.encode()).digest())
                            print ("[+] Clave de sesión AES generada (hexadecimal):", clave_sesion.hex())
                            criptograma = cifrar_aes(texto_claro,clave_sesion)
                            print ("[+] Texto claro cifrado con la clave de sesión AES (hexadecimal):", criptograma.hex())
                            # Se lee la clave privada del emisor, el módulo (n) y el exponente de
                            # la clave privada (d), para firmar digitalmente el texto en claro.
                            datos_clave_privada = clave_rsa("private_emisor.pem")
                            print ("[+] Clave privada del emisor:")
                            print ("    Módulo (n):", datos_clave_privada.n)
                            print ("    Exponente clave privada (d):", datos_clave_privada.d)
                            h = (hashlib.sha256(texto_claro.encode()).digest())
                            print ("[+] Firma digital del texto en claro (hexadecimal):", h.hex())
                            s = exp_modular_rapida(int(h.hex(),16), datos_clave_privada.d, datos_clave_privada.n)
                            print ("[+] Firma digital cifrada con la clave privada RSA del emisor (hexadecimal):", hex(s)[2:])
                            # Se lee la clave pública del receptor, el módulo (n) y el exponente de
                            # la clave pública (e), para cifrar la clave de sesión AES.
                            datos_clave_publica = clave_rsa("public_receptor.pem")
                            print ("[+] Clave pública del receptor:")
                            print ("    Módulo (n):", datos_clave_publica.n)
                            print ("    Exponente clave pública (e):", datos_clave_publica.e)
                            c = exp_modular_rapida(int(clave_sesion.hex(),16), datos_clave_publica.e, datos_clave_publica.n)
                            print ("[+] Clave de sesión cifrada con la clave pública RSA del receptor (hexadecimal):", hex(c)[2:])
                            f_comunicacion = open("comunicacion.txt", "w")
                            f_comunicacion.write((b64encode(str(c).encode())).decode() + "\n")
                            f_comunicacion.write(b64encode(criptograma).decode() + "\n")
                            f_comunicacion.write((b64encode(str(s).encode()).decode()))
                            f_comunicacion.close()
                        else:
                            print ("*** ERROR: Por favor, introduzca la clave.")
                else:
                    print ("*** ERROR: Por favor, introduzca el texto en claro a cifrar.")
        elif opcion == "2":
            print ("*** FIN ******************************************")
            salir = True
        else:
            print ("*** ERROR: Opción no válida.")

if __name__ == '__main__':
    main()

Lo ejecuto:

Tras introducirse el mensaje o texto en claro a cifrar, lo primero que hace el script es generar la clave AES, calculando el hash SHA-256 de la clave introducida por el usuario, con la que se cifra el texto en claro y se obtiene el criptograma o texto en claro cifrado (criptografía simétrica).

Lo siguiente que hace el script es leer la clave privada del emisor (módulo y exponente de la clave privada), almacenada en formato PEM en un fichero, para cifrar mediante el algoritmo RSA (criptografía asimétrica) la firma digital del mensaje o texto claro, previamente calculada mediante la obtención del hash SHA-256 de este último.

Posteriormente, el script lee la clave pública del receptor (módulo y exponente de la clave pública), almacenada en formato PEM en otro fichero, para cifrar mediante el algoritmo RSA (criptografía asimétrica) la clave de sesión AES con la que se ha cifrado el mensaje o texto en claro (criptografía simétrica).

Y, finalmente, para simular el envió de la comunicación, se genera un fichero, comunicación.txt, en el que se almacenan, codificados en base64, lo siguientes datos: la clave de sesión AES cifrada, el criptograma y la firma digital cifrada. 

De esta forma, cuando el receptor reciba la comunicación, lo primero que se hará será descifrar la clave de sesión AES con su clave privada (criptografía asimétrica) y con ésta descifrar el criptograma (criptografía simétrica), para, posteriormente, verificar la firma digital del mensaje o texto en claro con la clave pública del emisor (criptografía asimétrica).

En un post posterior pondré el script en python para implementar esto último.

Quizás también te interese:

Comentarios

Entradas populares de este blog

Criptografía (I): cifrado Vigenère y criptoanálisis Kasiski

Hace unos días mi amigo Iñaki Regidor ( @Inaki_Regidor ), a quien dedico esta entrada :), compartió en las redes sociales un post titulado "Criptografía: el arte de esconder mensajes"  publicado en uno de los blogs de EiTB . En ese post se explican ciertos métodos clásicos para cifrar mensajes , entre ellos el cifrado de Vigenère , y , al final del mismo, se propone un reto consistente en descifrar un mensaje , lo que me ha animado a escribir este post sobre el método Kasiski  para atacar un cifrado polialfabético ( conociendo la clave descifrar el mensaje es muy fácil, pero lo que contaré en este post es la forma de hacerlo sin saberla ). El mensaje a descifrar es el siguiente: LNUDVMUYRMUDVLLPXAFZUEFAIOVWVMUOVMUEVMUEZCUDVSYWCIVCFGUCUNYCGALLGRCYTIJTRNNPJQOPJEMZITYLIAYYKRYEFDUDCAMAVRMZEAMBLEXPJCCQIEHPJTYXVNMLAEZTIMUOFRUFC Como ya he dicho el método de Vigenère es un sistema de sustitución polialfabético , lo que significa que, al contrario que en un sistema de

¿Qué significa el emblema de la profesión informática? (I)

Todas o muchas profesiones tienen un emblema que las representa simbólicamente y en el caso de la  informática: " es el establecido en la resolución de 11 de noviembre de 1977  para las titulaciones universitarias superiores de informática, y  está constituido por una figura representando en su parte central  un  núcleo toroidal de ferrita , atravesado por  hilos de lectura,  escritura e inhibición . El núcleo está rodeado por  dos ramas : una  de  laurel , como símbolo de recompensa, y la otra, de  olivo , como  símbolo de sabiduría. La  corona  será la  de la casa real  española,  y bajo el escudo se inscribirá el acrónimo de la organización. ". Veamos los diferentes elementos tomando como ejemplo el emblema del COIIE/EIIEO (Colegio Oficial de Ingenieros en Informática del País Vasco/ Euskadiko Informatikako Ingeniarien Elkargo Ofiziala ) . Pero no sólo el COIIE/EIIEO adopta el emblema establecido en dicha resolución, sino que éste se adopta también como im

Criptografía (XXIII): cifrado de Hill (I)

En este post me propongo explicar de forma comprensible lo que he entendido sobre el cifrado de Hill , propuesto por el matemático Lester S. Hill , en 1929, y que se basa en emplear una matriz como clave  para cifrar un texto en claro y su inversa para descifrar el criptograma correspondiente . Hay tres cosas que me gustan de la criptografía clásica, además de que considero que ésta es muy didáctica a la hora de comprender los sistemas criptográficos modernos: la primera de ellas es que me "obliga" a repasar conceptos de matemáticas aprendidos hace mucho tiempo y, desgraciadamente, olvidados también hace demasiado tiempo, y, por consiguiente, que, como dice  Dani , amigo y coautor de este blog, me "obliga" a hacer "gimnasia mental"; la segunda es que, en la mayoría de las ocasiones, pueden cifrarse y descifrase los mensajes, e incluso realizarse el criptoanálisis de los criptogramas, sin más que un simple lápiz y papel, es decir, para mi es como un pasat