Thursday 30 October 2014

RSA basado en firmas digitales

Repositorio de claves publicas

Para esta penúltima tarea se pidió implementar a través de comunicación por sockets un repositorio de claves públicas, así como la transmisión de correos cifrados.

Los requisitos además de los mencionados, es autenticar que tanto como cliente y servidor sean ellos mismos, el cliente pueda hacer consulta al servidor de clave pública de algún usuario inscrito en su archivo. Todo esto se hará utilizando RSA como autenticación (para no repetir como funciona por favor checa este post).


Funciones

Lado del cliente

En el lado del cliente se creó un objeto de la clase cliente. La clase cliente cuenta con los siguientes métodos: Genera n, φ(n), e y d:

    def __init__(self,p,q):
        self.p = p
        self.q = q

    def genN(self):
        print 'p = ', self.p
        print 'q = ', self.q
        print 'n = ', self.p * self.q
        return (self.p * self.q)

    def genphiN(self):
        print 'phiN = ',((self.p - 1) * (self.q - 1))
        return ((self.p - 1) * (self.q - 1))

    def genE(self,n,phiN):
        while True:
            e = random.SystemRandom().randint(2,n-1)
            print 'e = ', e
            if funciones.gcd(max(phiN,e),min(phiN,e)) == 1:
                return e
           
    def genD(self,phiN,e):
        r, x, y = funciones.gcdExt(phiN,e)
        if y < 1:
            y = y + phiN
        return y
    

El siguiente método llama a todos los métodos anteriores, este método es llamado si es que nunca se ha creado una clave y la guarda en un archivo llamado claves.txt (almacenado en el lado del cliente). Si ya se ha creado abre un archivo, y almacena en el programa los valores de e, d y n.

    def generarClave(self):
        n = self.genN()
        phiN = self.genphiN()
        while True:
            e = self.genE(n,phiN)
            d = self.genD(phiN,e)
            if d != 1:
                print 'e = ',e
                print 'd = ',d
                print 'n = ',n
                break
        return n,phiN,e,d

    # solo llamo a este metodo si nunca antes habia 
    # entrado al servidor, es decir que no tengo el archivo
    def guardarClave(self,e,d,n):
        f = open('clave.txt','w')
        f.write(str(e))
        f.write('\n')
        f.write(str(d))
        f.write('\n')
        f.write(str(n))
        f.write('\n')
        f.close()
    
Lo que mencione anteriormente sucede en el archivo cliente.py el cual llama a la función claveCliente().

# creando la clave del usuario
def claveCliente():
    try:
        f = open('clave.txt')
        e = int(f.readline())
        d = int(f.readline())
        n = int(f.readline())
        f.close()
    except:
        p = funciones.genNumPriRan(1000,9999)
        q = funciones.genNumPriRan(1000,9999)
        cl = Cliente(p,q)
        n,phiN,e,d = cl.generarClave()
        cl.guardarClave(e,d,n)
    return e,d,n
 

Subscripción

Lado cliente. Cuando se ejecuta el script cliente.py. Se tiene que propocionar el usuario, solo si ha accedido con anterioridad al servidor. Si es la primera vez, entonces el cliente debe de proporcionar su usuario, después el servidor le solicitará un correo electrónico.

# solo es una verificacion de cuando se ejecute el programa
def ingresoUsuario(argv):
    if len(argv) < 2:
        print 'Si estas o no registrado escribe tu usuario'
        while True:
            usuario = raw_input('> ')
            if usuario != '' and usuario != 'q' and usuario != 'quit()' and usuario != 'exit':
                break
    else:
        usuario = argv[1]
    return usuario

# creando la clave del usuario                                                                                                                        
def claveCliente():
    try:
        f = open('clave.txt')
        e = int(f.readline())
        d = int(f.readline())
        n = int(f.readline())
        f.close()
    except:
        p = funciones.genNumPriRan(1000,9999)
        q = funciones.genNumPriRan(1000,9999)
        cl = Cliente(p,q)
        n,phiN,e,d = cl.generarClave()
        cl.guardarClave(e,d,n)
    return e,d,n
 

Llamando las funciones anteriores.

    usuario = ingresoUsuario(argv)
    # si no existe el archivo entonces lo crea si no solo lo lee                                                                       
    e,d,n = claveCliente()
    cliente.send(usuario+'|'+str(e)+'|'+str(n))
    aceptoUsuario = cliente.recv(1024)
    print aceptoUsuario
    if aceptoUsuario == "No existes en mi BD dame tu correo":
        correo = raw_input("Introduce tu correo\n> ")
        cliente.send(correo)
        print 'Haz sido anadido al repositorio vuelve a iniciar sesion'
    elif aceptoUsuario == "Lo siento pero el usuario que quieres registrar ya existe\no haz cambiado tu clave publica.":
        cliente.close()


Lado servidor. El servidor recibe el usuario, después abre su archivo de usuarios.txt y compara si las claves enviadas coinciden con el usuario enviado. 
    usuario = ingresoUsuario(argv)
# lo que hace es verificar que el usuario que mande coincida con
# la clave que tenemos en nuestro registro, si no existe
# mandamos solicitar el email                                                                                            #http://stackoverflow.com/questions/16222956/reading-a-file-line-by-line-into-elements-of-an-array-in-python?l\q=1                                                                                                                                                            
def verificar(sc,usuario,eClte,nClte):
    f = open('usuarios.txt')
    for line in f:
        datos = line.split("|")
        if usuario == datos[0]:
            if eClte == int(datos[2]) and nClte == int(datos[3]):
                print 'si estas en la bd'
                print 'correo: ', datos[1]
                f.close()
                return "OK"
            else:
                f.close()
                return "En uso"
    return "No existe"

# si existe comparamos su clave publica con la del repo, si no coinciden lo sacamos o le decimos que su clave ha cambiado                                                                                                                                                     
def verificarClaveUsuario(sc,usuario,eClte,nClte):
    print 'verificando clave de usuario y si no existe lo agrego'
    verifica = verificar(sc,usuario,eClte,nClte)
    if verifica == "No existe":
        # no existe y hay que registrarlo                                                                                                                                                                                                                                     
        sc.send("No existes en mi BD dame tu correo")
        correo = sc.recv(1024)
        cadena = usuario+"|"+correo+"|"+str(eClte)+"|"+str(nClte)+"\n"
        f = open('usuarios.txt','a')
        f.write(cadena)
        f.close()
        return False
    elif verifica == "OK":
        sc.send("OK")
        return True
    else:
        sc.send(str("Lo siento pero el usuario que quieres registrar ya existe\no haz cambiado tu clave publica."))
        return False

Autenticación


Lado cliente
 # servidor me reta (reta al cliente)                                                                                                          
 xS = str(cliente.recv(512))
 print 'xS = ', xS
 xSDecifrada = funciones.firmarCifrar(int(xS),d,n,eSrv,nSrv)
 print 'Decifrada xS = ', xSDecifrada
 fxS = funciones.f(xSDecifrada)
 print 'fxS = ', fxS
 respuesta = funciones.firmarCifrar(int(fxS),d,n,eSrv,nSrv)
 print 'fxSCifrada = ',respuesta
 cliente.send(str(respuesta))
 # esperamos respuesta al reto del servidor                                                                                                    
 print "que dices server ",cliente.recv(512)

 # retamos al servidor                                                                                                                         
 x = funciones.genNumPriRan(100,999)
 print 'Mi reto como cliente'
 print 'x = ',x
 fx = funciones.f(x)
 print 'fx = ',fx
 # firmo y cifro x                                                                                                                             
 xCrfd = funciones.firmarCifrar(x,d,n,eSrv,nSrv)
 print 'xCF = ', xCrfd
 cliente.send(str(xCrfd))

 # esperamos por respuesta del server                                                                                                          
 retoAceptado = int(cliente.recv(512))
 print 'fxcifrada(envioServer) = ', retoAceptado
 retoAceptado = funciones.firmarCifrar(retoAceptado,d,n,eSrv,nSrv)
 print 'respuesta = ',retoAceptado
 if retoAceptado == fx:
      print 'ok, es el servidor'
 
 
 
  
 reto = int(sc.recv(512))
            print '\n\nacepto reto funcion\n\n'
            print 'reto = ',reto
            # servidor acepta reto decifrado y quitando la firma                                                                                                                                                                 
            xClte = funciones.firmarCifrar(reto,d,n,eClte,nClte)
            print 'xClte = ',xClte
            # aplico funcion f(X) al reto                                                                                                                                                                                        
            fxClte = funciones.f(xClte)
            print 'fxClte = ',fxClte
            # cifro el reto                                                                                                                                                                                                      
            fxFC = funciones.firmarCifrar(fxClte,d,n,eClte,nClte)
            print 'reto cifrado = ',fxFC
            # devuelvo el reto                                                                                                                                                                                                   
            sc.send(str(fxFC))
            # espero respuesta                                                                                                                                                                                                   
            respuesta = sc.recv(512)
Lado servidor

codigo
nada
#!/usr/bin/python
import funciones
from clscliente import Cliente
from socket import socket
from time import sleep
from sys import argv

def main():
    usuario = ''
    if len(argv) < 2:
        print 'Escribe tu nombre de usuario, si no cuentas con un usuario, escribelo y te registraremos'
        while True:
            usuario = raw_input('> ')
            if usuario != '' and usuario != 'q' and usuario != 'quit()' and usuario != 'exit':
                break            
    else:
        usuario = argv[1]
    # si no existe el archivo entonces lo crea si no solo lo lee
    try:
        f = open('clave.txt')
        e = int(f.readline())
        d = int(f.readline())
        n = int(f.readline())
    except:
        p = funciones.genNumPriRan(100,999)
        q = funciones.genNumPriRan(100,999)
        cl = Cliente(p,q)
        n,phiN,e,d = cl.generarClave()
        cl.guardarClave(e,d,n)
    # primero que nada tengo que establecer comunicacion con el server para poder mandar reto
    cliente = socket()
    cliente.connect(('127.0.0.1',57712))
    print cliente.recv(512) # mensaje de bienvenida
    # recibo e y n del servidor
    datos = cliente.recv(512)
    datos = datos.split("|")
    eSrv = int(datos[0])
    nSrv = int(datos[1])
#    print 'eSrv = ',eSrv
#    print 'nSrv = ',nSrv
    # envio mi publica y usuario
    cadena = usuario+'|'+str(e)+'|'+str(n)
    cliente.send(cadena) 

    # reto al servidor
    x = funciones.genNumPriRan(10,99)
    fx = funciones.f(int(x))
    # firmo y cifro x
    xfrmCrfd = funciones.firmarCifrar(x,d,n,eSrv,nSrv)
    print 'x = ',x
    print 'xCF = ', xfrmCrfd
    print 'fx = ',fx

    # envia reto
    cliente.send(str(xfrmCrfd))

    retoAceptado = int(cliente.recv(1024))
    print 'fxcifrada(envioServer) = ', retoAceptado
    retoAceptado = funciones.firmarCifrar(retoAceptado,d,n,eSrv,nSrv)
    print 'respuesta = ',retoAceptado
    if retoAceptado == fx:
        print 'ok'
    else:
        print ' no coincide'
        cliente.close()

        

#     xS = cliente.recv(512)
#     xS = int(xS)
#     print 'xS = ', xS
#     cliente.send(cadena) # mi publica
#     cliente.close()

#     # operacion contraria
#     quitaFrm = funciones.expModRap(frmCfrd,dSrv,nSrv)
#     encX = funciones.expModRap(quitaFrm,e,n)
#     print 'es la x = ',encX
#     fx = funciones.f(x)
# #    print fx
    
    
main()

Petición de clave


Lado de cliente
# solicita una clave publica
if msj == "-d": # -dime la clave publica                                                                                                                                                                                                                                                                                         
     cliente.send("-d")
     sleep(1)
     usuario = raw_input('De quien quieres saber la clave publica\n> ')
     print usuario
     cliente.send(usuario)
     datos = cliente.recv(1024).split("|")
     if datos[0] != "Usuario no existe":
          correoR,eR,nR = datos[0],datos[1],datos[2]
          print 'correo: ', correoR
          print '     e: ', eR
          print '     n: ', nR
     else:
     print 'Usuario no existe'


Lado del servidor
# pregunta por un usuario y regresa su publica y correo                                                        
def preguntar(usuario):
    f = open('usuarios.txt')
    for line in f:
        datos = line.split("|")
        print datos
        if usuario == datos[0]:
            f.close()
            return datos[1],datos[2],datos[3] # correo,e,n                                                     
    # si no regresa nada es que no existe entonces no se puede hacer nada                                      
    f.close()
    return None,None,None
 
if datos == "-d" or datos == "-e":
     usuario = sc.recv(1024)
     print usuario
     correo,e,n = preguntar(usuario)
     print correo
     print e
     print n
     if correo != None:
          cadena = correo+"|"+str(e)+"|"+str(n)
          print cadena
          sc.send(str(cadena))
     else:
          sc.send("Usuario no existe")


Facilidad de uso

Cuando el cliente se conecta al servidor, este le muestra texto de ayuda que le dice que debe hacer dependiendo de la situación que quiera generar.

def bienvenida(sc):                                                                                                                                                                                                              
    sc.send(str('-d     proporciona la clave publica del usuario solicitado\n'))
    sc.send(str('-e     cifrar un mensaje al usuario que elegiste\n'))
    sc.send(str('-m     decifrar un mensaje cifrado\n'))
    sc.send(str('-q     desconectarte del servidor\n'))
    sc.send(str('-h     Ayuda. muestra el texto anterior\n'))
    sleep(1)

Y en el lado del cliente, cuando este haciendo peticiones, para mostrar la ayuda se hace una llamada a la función ayuda() del lado del cliente.

def ayuda():
    print '-d     proporciona la clave publica del usuario solicitado\n'
    print '-e     cifrar un mensaje al usuario que elegiste\n'
    print '-m     decifrar un mensaje cifrado\n'
    print '-q     desconectarte del servidor\n'
    print '-h     Ayuda. muestra el texto anterior\n'


Cifrado y descifrado



def cifrarMensaje(eRcpt,nRcpt):
    mensaje = raw_input('Mensaje: ')
    c =''
    for i in xrange(len(msj)):
        if ord(msj[i]) < 100:
            aux = "0" + str(ord(msj[i]))
            c += str(expModRap(int(aux),eRcpt,nRcpt)).zfill(8) # el tamano de bloque es 8 porque las n tienen tamano de hasta 8                                       
        else:
            c += str(expModRap(int(ord(msj[i])),int(eRcpt),int(nRcpt))).zfill(8)
    f = open('mensajecifrado.txt','w')
    f.write(c)
    f.close()

def decifrarMsj(dRcpt,nRcpt):
    f = open('mensajecifrado.txt','r')
    msjCfr = f.readline()
    f.close()
    j = 0
    letra = ""
    msj = ""
    for i in xrange(len(c)):
        j = j + 1
        letra = letra+ c[i]
        if j == 8:
            j = 0
            msj += str(chr(expModRap(int(letra),int(dRcpt),int(nRcpt))))
            letra = ""
    print "texto decifrado:\n",decifro
    f = open('mensaje.txt','w')
    f.write(msj)
    f.close()
 
if msj == "-e": # -envia el mensaje cifrado                                                                                                           
     cliente.send("-e")
     sleep(1)
     sabes = raw_input('Conoces la clave publica y correo del remitente[S/N]\n> ')
     if sabes == 'S' or sabes == 's':
          # se llama al metodo para cifrar todo de lado de cliente                                                                                      
          eR = raw_input('e del remitente\n> ')
          nR = raw_input('n del remitente\n> ')
   cifrarMsj(eR,nR)          
     else:
          usuario = raw_input('Nombre de usuario del remitente\n> ')
          print usuario
          cliente.send(usuario)
          datos = cliente.recv(1024).split("|")
          if datos[0] != "Usuario no existe":
               correoR,eR,nR = datos[0],datos[1],datos[2]
               cifrarMsj(eR,nR)
          else:
               print "Usuario no existe"
 

Código

cliente.py
clscliente.py
funciones.py
servidor.py

Referencias.



González, R. (S/A).  Python para todos. España. En este libro se consulto lo referente a comunicación entre sockets. Disponible en pdf en: https://launchpadlibrarian.net/18980633/Python%20para%20todos.pdf

 Revise el blog de Víctor Ríos, ya que al momento de estar descifrando un texto no estaba obteniendo el resultado esperado, entonces compare mi código con el de él y basicamente el cifrado que el hace con su código, mi parte del código hacía lo mismo, pero al desencriptar estaba olvidando establecer un tamaño fijo por bloque de cada letra para poder descifrar, porque si no, no iba a funcionar. Además cuando yo cifraba el texto quería pasarlo a ASCII y me daba errores, obviamente porque cuando cifraba obtengo un número que sobrepasa los caracteres del ASCII. 

1 comment:

  1. + subscription
    + one-way auth.
    + two-way auth.
    + query
    + encryption
    + decryption
    + ease of use

    Hay algo raro con tu incrustación de código, bloques vacíos de una sola línea en blanco.

    7 de 10 pts

    ReplyDelete