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
codigonada
#!/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.
+ subscription
ReplyDelete+ 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