Criptografía en Python - RSA

Índice

    ¿Qué es RSA?


    Adi Shamir, uno de los tres inventores de RSA (los otros dos son
    Ron Rivest
    y
    Leonard Adleman).
    En criptografía, RSA (siglás de Rivest, Shamir y Adleman) es un sistema
    criptográfico de clave pública desarrollado en 1979, siendo el primer y el más
    utilizado algoritmo de esta índole registrado hasta la fecha. Es capaz incluso
    de cifrar y firmar digitalmente.
    Como en todo sistema de clave pública, el emisor requiere la clave pública del
    receptor para cifrar los datos a enviar, a posteriori, el receptor deberá usar
    su clave privada para descifrarlos. Una cosa peculiar es que para el caso de
    firmar los datos, el emisor usa su propia clave privada para firmarlos y luego
    el receptor usar la clave pública del emisor para verificarlos.

    La Librería pycryptodome

    python no es solamente conocido por tener una sintaxis legible por humanos,
    también se le conoce por la cantidad excesivas (aunque útiles) de librerías,
    módulos y demás. pycryptodome es una librería que utiliza primitivas
    criptográficas de bajo-nivel, está escrita mayormente en python, pero para
    uso crítico (como el rendimiento) se utilizan algunas extensiones escritas
    en C.
    pycryptodome en una bifurcación de la vieja librería pycrypto, que trae con
    sí, las siguientes mejoras:
    • Modos de cifrado autenticados (GCM, CCM, EAX, SIV, OCB)
    • AES acelerado en plataformas Intel a través de AES-NI
    • Soporte de primera clase para PyPy
    • Criptografía de curvas elípticas (solo curvas NIST P-256, P-384 y P-521)
    • API mejor y más compacta (atributos nonce y
      iv para cifrados, generación automática de nonces e IVs
      aleatorios, modo de cifrado CTR simplificado y más)
    • SHA-3 (incluidos los XOF SHAKE), algoritmos hash SHA-512 y BLAKE2
      truncados
    • Cifrados de flujo Salsa20 y ChaCha20/XChaCha20
    • Poly1305 MAC
    • Cifrados autenticados ChaCha20-Poly1305 y XChaCha20-Poly1305
    • funciones de derivación scrypt, bcrypt y HKDF
    • Deterministic (EC)DSA
    • Contenedores de claves PKCS-8 protegidos con contraseña
    • Esquema de intercambio secreto de Shamir
    • Los números aleatorios consiguen originados directamente del sistema
      operativo (y no de un CSPRNG [Generador de Números Criptográficamente
      Seguros, en español] en el espacio de usuario)
    • Proceso de instalación simplificado, que incluye un mejor soporte para
      Windows
    • Generación de claves RSA y DSA más limpia (basada en gran medida en FIPS
      186-4)
    • Limpieza importante y simplificación de la base de código

    Instalación de pycryptodome

    Al igual que su uso, su instalación no tarda mucho más que ejecutar un
    simple comando:
    python3 -m pip install pycryptodome 

    También es recomendable no tener instalado pycrypto, que como se
    mencionó pycryptodome, es una bifurcación mucho mejor de él. En
    caso de tenerlo instalado, igualmente con un simple comando se debería
    desinstalar:

    python3 -m pip uninstall pycrypto
    

    OJO: Todo el tutorial estará hecho para la versión 3 y se debe
    tener instalado el paquete python-dev y
    build-essential para su versión de python correspondiente, use su
    gestor de paquetes en caso de no tenerlo instalado. Como generalmente se
    usan distribuciones basadas en Debian o Arch Linux, aquí unos simples
    comandos orientativos:

    # Debian
    sudo apt-get install build-essential python3-dev
    # Arch Linux
    sudo pacman -S base-devel
    
    # General
    python3 -m pip install pycryptodome
    

    Generando el par de claves

    Una vez hemos aprendido los conceptos básicos, además de instalar lo
    correspondiente, ahora pasemos a la práctica. En este caso primero vamos a
    generar el par de claves, que como ya saben, serían la clave pública y la
    clave privada. Empecemos:

    import sys
    from Crypto.PublicKey import RSA # Importamos el módulo RSA
    
    # El usuario (o sea nosotros) tiene que pasar un número mayor
    # o igual 1024 y usando el objeto 'int' convertirmos un string
    # a un entero
    . bit_size = int(sys.argv[1]) key_format = sys.argv[2]

    # Generamos el par de claves. Dependiendo del tamaño y el
    # procesamiento de nuestro computador es lo que podrá tardar
    .
    keys = RSA.generate(bit_size) print("Clave Pública:") # Exportamos la clave pública y la imprimimos. Colocamos como
    # argumento 'nn' en el parámetro 'end' de la función 'print'
    # para imprimir dos saltos de líneas y se vea más legible.
    #
    # 'key_format' se explicará en unos instantes
    #
    # Usamos el método '.decode(...)' porque al exportarlos estarán
    # en 'bytes' y es mejor (para volverlo legible) tenerlo en 'utf-8'.
    print(keys.publickey().export_key(key_format).decode(), end='nn') print("Clave Privada:") # Hacemos prácticamente lo mismo que lo anterior, pero a diferencia
    # de la exportación de la clave pública, no se necesita explicitar
    # ningún método.
    print(keys.export_key(key_format).decode())

    El código está comentado para que vayan comprendiendo cada cosa. Ahora
    pasemos a ejecutar el script para verlo detenidamente.
    No solo es recomendable, es un deber el usar claves mayor o igual que
    2048, 1024 ya se considera inseguro, tampoco hay que abusar del tamaño, yo
    recomiendo 2048 o 3072 bits, aunque la siguiente tabla enumera el tamaño
    de algunos algoritmos para mantener una seguridad mayor a 2030:
    Sobre los formatos, existen tres formatos para usar con pycryptodome: PEM
    (el predeterminado) que es la codificación basada en texto (útil si se
    quiere compartir por algún servicio como correo electrónico); OpenSSH,
    también es una codificación textual que sigue la especificación OpenSSH;
    por último, DER, codificación binaria.
    Usando el formato OpenSSH con un tamaño 2048 bits como el tamaño del
    par de claves

    ¡Oh no!, ha ocurrido un error inesperado
    En la última imagen se puede apreciar claramente un error, un carácter
    inválido para 'utf-8', pero simplemente quitando el método
    '.decode(...)' de la línea 13 y 17, permitiría mostrar los datos:

    Cifrado con RSA

    No tiene mucho sentido simplemente generar el par de claves,
    pycryptodome nos permite cifrar y descifrar datos, como se podrá ver a
    continuación:
    Cifrando y descifrando el texto "hola"

    Aquí el script:
    import sys
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_OAEP
    
    bit_size = int(sys.argv[1])
    key_format = sys.argv[2]
    # El usuario (o sea nosotros) pasaremos un texto
    # arbitrario para después cifrarlo y descifrarlo
    . text2cipher = sys.argv[3] keys = RSA.generate(bit_size) # Importamos la clave pública para cifrar los datos cipher_rsa = PKCS1_OAEP.new(keys.publickey()) # Importamos la clave privada para descifrar los datos decipher_rsa = PKCS1_OAEP.new(keys) # Ciframos los datos. # # Se deben codificar los datos a 'utf-8', por eso está
    # presente el método '.encode(...)'
    enc_data = cipher_rsa.encrypt(text2cipher.encode()) # Desciframos los datos dec_data = decipher_rsa.decrypt(enc_data) print("Encriptado:") print(enc_data, end='nn') print("Desencriptado:") print(dec_data)
    Básicamente es el mismo que el primero, y se comentó solo lo necesario para que se tenga una mejor compresión. Por cierto, el script no es muy realista en cuanto a lo que se hace en la vida real, ya que cada vez que lo ejecutemos se va a generar el par de claves y luego se va a cifrar y descifrar el dato. En las próximas entregas se mostrarán algunos trucos para realizar un script realista a lo que se hace en la vida real (como almacenar claves, importarlas, etcétera).
    También tengo que aclarar que no se puede cifrar o descifrar datos que fueron codificados con una clave de tamaño superior a la de su generación, pero no ha de haber preocupaciones, en las próximas entregas se enseñará cómo quitar esa limitación.

    Lecturas recomendadas

    Los siguientes enlaces les servirán si quieren aprender mucho más y prepararse fluidamente para lo que vendrán en las siguientes partes:
    • https://pycryptodome.readthedocs.io
    • https://es.wikipedia.org/wiki/RSA
    • https://es.qwe.wiki/wiki/Key_size
    Me despido, espero hayan aprendido tanto como yo lo hice.
    ~ DtxdF

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Te has suscrito correctamente al boletín

    Se produjo un error al intentar enviar tu solicitud. Inténtalo de nuevo.

    Mi Diario Python will use the information you provide on this form to be in touch with you and to provide updates and marketing.