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étrica) si 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
Publicar un comentario