Menú Cerrar
criptografía

En esta entrada se desarrolla una aplicación en Python para proveer servicios como integridad, confidencialidad y no repudio. Mediante el uso de la criptografía de llave simétrica, criptografía de llave asimétrica, funciones hash, firma digital, sobre digital y certificado digital.

La digitalización de la información se ha vuelto tendencia. Las transacciones en línea se vuelven cada vez más populares. Las empresas guardan los datos confidenciales de sus clientes en formato digital. Los hospitales guardan los expedientes clínicos de los clientes en formato digital. En todas las entidades la tendencia es no hacer papelería y migrar a la digitalización. En el mundo de la seguridad informática los hackers han desarrollado habilidades sofisticadas para acceder a estos datos confidenciales. Por otro lado, los profesionales destinados a proteger los datos al acceso no autorizado han desarrollado habilidades aún más sofisticadas para proporcionar mecanismos que garanticen algunas medidas de seguridad para proteger los datos en reposo, en tránsito y/o en uso.

Criptografía

Vamos a definir las palabras claves antes de pasar a código.

Criptografía

Criptografı́a tiene su raı́z de la lengua griega, proviene de las palabras (kryptós = recubierto, oculto) y (grafein = escribir) a las que agregando el sufijo -ía se le confiere el carácter de conocimiento o ciencia. Entonces, etimológicamente la criptografía es la ciencia que estudia la escritura oculta. Según la misma fuente, la criptografía también se puede definir como el estudio de diseñar cifradores.

Los cifradores se dividen en dos tipos, los de llave simétrica y los de llave pública. Los cifradores de llave simétrica usan la misma llave para cifrar y descifrar los datos mientras que los de llave pública usan llaves diferentes.

Funciones hash

Mientras que los cifradores se usan para proveer la confidencialidad de los datos, las funciones hash se usan para proveer la integridad. Básicamente el hash representa la huella digital del documento y así, cualquier modificación de los datos se puede saber porque esta firma es única.

Criptografía de llave simétrica

La criptografía de llave simétrica (SKC) consiste en la utilización de una sola llave tanto para el cifrado como el descifrado de un mensaje.

Criptografía de llave asimétrica

La criptografía de llave asimétrica o pública (PKC) consiste en el uso de una llave privada y una llave pública para el cifrado y el descifrado de un mensaje. En este tipo de criptografía, el módulo emisor cifra el mensaje con la llave pública del receptor y el receptor usa su llave privada para descifrar el mensaje.

Sobre digital

Un sobre digital es un método de envío de llave simétrica que usa la criptografía de llave asimétrica, se cifra el sobre con la llave pública del receptor y el receptor usa su llave privada para descifrar el sobre y recupera la llave simétrica. En la figura se ilustra el proceso de cifrado y de descifrado del sobre digital.

Firma digital

La firma digital es una forma de cifrar la huella digital de un mensaje, el emisor cifra la huella con su llave privada y el receptor usa la llave pública del emisor para verificar la firma. En la figura se ilustra el proceso de cifrado y de descifrado del sobre digital.

Las actividades del emisor y del receptor se presentan en las siguientes dos imágenes.

Criptografía
Emisor
Criptografía
Receptor

Primero vamos a crear el certificado con el siguiente código.

import datetime

from cryptography import x509
from cryptography.x509.oid import NameOID

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_public_key
from cryptography.hazmat.primitives.asymmetric import padding


class Certificate():
    def __init__(self,private_key,public_key):
        subject = issuer = x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u"nombre_del_país"),
                              x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, u"provincia"),
                              x509.NameAttribute(NameOID.LOCALITY_NAME, u"Ciudad"),
                              x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Organización"),
                              x509.NameAttribute(NameOID.COMMON_NAME, u"correo")])

        self.cert = x509.CertificateBuilder().subject_name(subject).issuer_name(issuer
                                       ).public_key(public_key
                                       ).serial_number(x509.random_serial_number()
                                       ).not_valid_before(datetime.datetime.utcnow()
                                       ).not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=100)
                                       ).add_extension(x509.SubjectAlternativeName([x509.DNSName(u"localhost")]),critical=False
                                       ).sign(private_key, hashes.SHA256(), default_backend())

    def getCertificate(self):
        return self.cert

    def validateCertificate(self, cert, public_key):
        try:
           public_key.verify(cert.signature,cert.tbs_certificate_bytes,padding.PKCS1v15(),cert.signature_hash_algorithm)
        except:
            return False
        else:
            return True

El algoritmo de AES.

from Crypto import Random
from Crypto.Cipher import AES
###KDF###
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA512
from Crypto.Random import get_random_bytes
###KDF###
from time import time
from os import listdir
import hashlib
import os

class AES_Cipher:

    ruta=None

    def __init__(self,ruta):
        super().__init__()
        self.ruta=ruta

    def pad(self,s):
        return s + b"\1" * (AES.block_size - len(s) % AES.block_size)

    def cifrar(self,message, key):
        message = self.pad(message)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return iv + cipher.encrypt(message)

    def descifrar(self,ciphertext, key):
        iv = ciphertext[:AES.block_size]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        texto_plano = cipher.decrypt(ciphertext[AES.block_size:])
        return texto_plano.rstrip(b"\1")

    def cifrar_archivo(self,archivo, key):
        print("Procesando cifrado...")
        with open("ruta_del_archivo", 'rb') as an:
            ahora = time()
            texto_plano = an.read()
        enc = self.cifrar(texto_plano, key)
        with open("ruta_del_archivo"+'.aes', 'wb') as an:
            an.write(enc)


    def decifrar_archivo(self,archivo, key):
        print("Procesando decifrado...")
        with open("ruta_del_archivo_cifrado"+'.aes', 'rb') as ac:
            texto_cifrado = ac.read()
        dec = self.descifrar(texto_cifrado, key)
        with open("ruta_para_guardar_el_archivo_descifrado", 'wb') as acz:
            acz.write(dec)

Algoritmo RSA.

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import utils

class RSA_Cipher:

    #Crifrando
    def cifrado(self, public_key, message):
        ciphertext = public_key.encrypt(
            message,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA512()),
                algorithm=hashes.SHA512(),
                label=None
            )
        )
        return ciphertext

    #Descifrando
    def descifrado(self, private_key, ciphertext):
        plaintext = private_key.decrypt(
            ciphertext,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA512()),
                algorithm=hashes.SHA512(),
                label=None
            )
        )

La clase principal.

#from Crypto.Cipher import PKCS1_OAEP
#from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
#from Crypto.Signature import PKCS1_v1_5
from AES_Cipher import * //Importar el primer archivo
from RSA_Cipher import * //Importar el segundo archivo
import Crypto
from os import listdir
from Certificate import *
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import utils
ruta = "Ruta del archivo"
from shutil import rmtree
firma=[]
longitud=3072

def generar_llaves(longitud):
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=longitud,
        backend=default_backend())
    public_key = private_key.public_key()
    return [private_key, public_key]

#Firmar archivos
def firmar_archivos(private_key,archivo):
    global acum_tiempof
    global acum_tamaf
    print("Firmando el archivo, favor de esperar...")
    chosen_hash = hashes.SHA512()
    hasher = hashes.Hash(chosen_hash, default_backend())
    with open("Ruta del archivo", "rb") as f:
        ahora = time()
        for bloque in iter(lambda: f.read(4096), b""):
            hasher.update(bloque)
    digest = hasher.finalize()
    signature = private_key.sign(
        digest,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA512()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        utils.Prehashed(chosen_hash))
    return signature

#Vericar firma
def verificar_firma(public_key, archivo, signature):
    print("Archivo: ",archivo)
    print("Verificando la firma del archivo, favor de esperar...")
    global acum_tiempovf
    global acum_tamavf
    chosen_hash = hashes.SHA512()
    hasher = hashes.Hash(chosen_hash, default_backend())
    with open(archivo, "rb") as f:
        ahora = time()
        for bloque in iter(lambda: f.read(4096), b""):
            hasher.update(bloque)
    digest = hasher.finalize()
    try:
        public_key.verify(
            signature,
            digest,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA512()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            utils.Prehashed(chosen_hash)
        )
    except Exception as e:
        print("La firma no es correcta")
    else:
        print("La firma es correcta")

def getData(ruta):
    archivos = listdir(ruta)
    #print(archivos)
    if '.DS_Store' in archivos:
        archivos.remove('.DS_Store')
    return archivos

if __name__ == "__main__":
    #Seguridad de la llave PKC
    longitud = 15360
    archivos = getData("Ruta del archivo")

    print("Generando la llave simétrica, favor de esperar...")
    password = b'contraseña secreta'
    salt = get_random_bytes(16)
    key_simetrica = PBKDF2(password, salt, 32, count=100000)
    print("¡Llave generada con éxito!")

    aes_c = AES_Cipher(ruta)
    for archivo in archivos:
        tasa_cifrado = aes_c.cifrar_archivo(archivo, key_simetrica)

    print("Generando las llaves de Alice, favor de esperar...")
    alice_kpriv, alice_kpub = generar_llaves(longitud)

    print("Generando el sobre digital, favor de esperar...")
    rsa_c = RSA_Cipher()
    sobre = rsa_c.cifrado(alice_kpub, key_simetrica)

    print("Gerando las llaves de Bob, favor de esperar...")
    bob_kpriv, bob_kpub = generar_llaves(longitud)

    print("Creando el certificado digital, favor de esperar...")
    cert = Certificate(bob_kpriv,bob_kpub)
    bob_cert = cert.getCertificate()

    firmas=[]
    for archivo in archivos:
        firma = firmar_archivos(bob_kpriv,archivo)
        firmas.append(firma)
    print("Cantidad de documentos firmados: ",len(firmas))

    print("Abriendo el sobre digital, favor de esperar...")
    key_simetrica_des = rsa_c.descifrado(alice_kpriv,sobre)

    print("Verificando el certificado, favor de esperar...")
    check_cert = cert.validateCertificate(bob_cert,bob_kpub)
    print("Certificado válido: ",check_cert)

    for archivo in archivos:
        tasa_descifrado=aes_c.decifrar_archivo(archivo, key_simetrica)

    for i in range(len(archivos)):
        firma = firmas[i]
        archivo = archivos[i]
        verificar_firma(bob_kpub, "Ruta del archivo a descifrar", firma)

Para correr la aplicación corre el archivo principal. Cualquier duda deja un comentario. ¡No olvides mostrar tu apoyo dejando un “Me gusta”!

Acerca del autor

Saintus Zephir

Ingeniero en Sistemas Computacionales y estudiante de Maestría en Ciencias en Ingeniería y Tecnologías Computacionales en el Centro de Investigación y de Estudios Avanzados del IPN (CINVESTAV-IPN).

Comparte el artículo en tus redes sociales

Artículos relacionados

28 comentarios

  1. Anónimo

    Lo probé y me funcionó, gracias me sirvió mucho :v

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

× ¿Cómo puedo ayudarte?