La implementación de un algoritmo RSA en una aplicación web, para evaluar la autentificación del ingreso de un id de usuario, el módulo con el que se trabaja y la llave o clave privada.
Implementación
Se utilizó la herramienta Apache, para utilizar Python cgi, en aplicaciones web, dentro de nuestra aplicación hacen un papel fundamental el programa en python que interactúa con la página html principal, nuestro algoritmo RSA, utilizado para la tarea anterior y la página escrita con html y javascript.
Autentificador en python:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python2.7 | |
# enable debugging | |
#El programa es producto de varias recopilaciones de rsa y ejemplos de cgi | |
#URLS: http://blog.hackxcrack.es/2011/11/introduccion-la-criptografia-moderna_09.html | |
#http://code.activestate.com/recipes/572196-rsa/ | |
#http://cic.puj.edu.co/wiki/doku.php?id=materias:laboratorio_de_lenguajes_ii:lableng2:ejemplos1 | |
from rsa_math import * #importamos algunas funciones para el algoritmo rsa como la generacion de primos | |
import cgi, cgitb #importamos las librerias para cgi | |
import random #importamos random | |
def generarclaves(bitn): #genera las claves dependiendo el numero de bits | |
p = genprime(bitn) | |
q = genprime(bitn) | |
n = p * q | |
phi = (p - 1) * (q - 1) | |
e=65537 | |
while (phi%e) == 0: | |
e=genprime(17) | |
d = inverse_mod(e, phi) | |
public = (n, e) | |
private = (n, d) | |
return public, private | |
# Convierte un string en un numero | |
def integear(s): | |
n = 0 | |
for c in s: | |
n <<= 8 | |
n += ord(c) | |
return n | |
def cifrar(s, public): | |
n = integear(s) | |
c = modex(n, public[1], public[0]) | |
return c | |
# Convierte un numero en un string | |
def stringear(n): | |
s = [] | |
while n > 0: | |
s.insert(0, chr(n & 255)) | |
n >>= 8 | |
return ''.join(s) | |
#descifra el mensaje con la llave privada | |
def descifrar(n, private): | |
p = modex(n, private[1], private[0]) | |
s = stringear(p) | |
return s | |
try: | |
import hashlib | |
except: | |
print >> sys.stderr, "No se ha encontrado el modulo Hashlib, no podras firmar ni comprobar firmas" | |
#funciones que realizan los signos del cifrado | |
def signo(msg,private): | |
s = int(hashlib.sha1(msg).hexdigest(), 16) | |
return modex(s,private[1],private[0]) | |
def checksigno(msg,sign,pub): | |
s = modex(sign,pub[1],pub[0]) | |
h = int(hashlib.sha1(msg).hexdigest(), 16) | |
return s == h | |
#algoritmo rsa | |
def guardarvalor(archivo, valor): #funcion que guarda un valor en un archivo | |
f1 = open(archivo, "w") #funcion que abre el archivo con permisos de escritura | |
f1.write(str(valor)) #se escribe el archivo como string | |
f1.close() #se cierra el archivo | |
def Abrirllaves(archivo, usuario): #funcion para leer llaves o claves | |
f1 = open(archivo, "r") #Se abre el archivo de solo lectura | |
for element in f1: | |
element = i.split(",") #se separa por comas entre cada valor en i | |
if element[0].lower() == usuario.lower() : #el usuario se pasa a minusculas | |
fl.close() #cerramos archivo | |
return int(element[1]), int(element[2]) #se devuelve el modulo y la llave privada | |
fl.close() | |
return False | |
def cambio(x): | |
return x*3+20; #Se ajusta un cambio en el valor de x. | |
def verificar(usuario, y): #funcion que checa los datos ingresados | |
valororiginal = cargarvalores("valores.dat") | |
if valororiginal == y: #los compara con los valores originales | |
return True #si se cumple retorna verdadero | |
else: | |
return False #si no, retorna falso | |
def cargarvalores(archivo): #Funcion utilizada para guardar valores en un archivo | |
f1 = open(archivo, "r") #abrimos el archivo con sólo lectura | |
valor = f1.read() #se obtiene todo el contenido del archivo | |
f1.close() #cerramos | |
return int(valor) #devuelve el valor entero del archivo | |
def probarRSA(bitn,s): | |
pub,priv=generarclaves(bitn) | |
c=cifrar(s,pub) | |
descifrar(c,priv) | |
f=signo(s,priv) | |
print "Comprobando firma...",checarsigno(s,f,pub) | |
def main(): | |
entrada = cgi.FieldStorage() #hacemos uso del cgi para interactuar con el formulario | |
if entrada.has_key("x") and entrada.has_key("usuario"): #Se verifica que se hayan llenado los campos de texto | |
lonx=int(entrada["x"].value) | |
probarRSA(lonx,str(entrada["usuario"].value)) | |
else: #si no se encuentran entradas | |
x = random.randint(1, 1000) #se generan aleatoriamente | |
guardarvalor("valores.dat", cambio(x)) #se guardan en el archivo valores y se hace un cambio en x para poder usarlo en el algoritmo | |
print x #imprimimos x | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Login</title> | |
</head> | |
<body> | |
<h1>Ingreso al Sistema</h1> | |
<p>Ingrese los siguientes datos: | |
</p> | |
<form name="datos" id="datos"> | |
<table> | |
<tr> | |
<th><code>Usuario</code></th> | |
<td><input type="text" size="10" name="usuario" id="usuario" value=""></td> | |
</tr> | |
<tr> | |
<th>Valor del modulo</th> | |
<td><input type="text" size="10" name="modulo" id="modulo" value=""></td> | |
</tr> | |
<tr> | |
<th>Valor de llave privada</th> | |
<td><input type="text" size="10" name="llave" id="llave" value=""></td> | |
</tr> | |
</table> | |
</form> | |
<h3>Estado del autentificador:</h3> | |
<div id="res"></div> | |
<script language="javascript"> | |
function potmod(x, pot, mod) | |
{ | |
if(n==1) | |
{ | |
return x; | |
} | |
else if(pot%2 ==0) | |
{ | |
return Math.pow(potmod(x, pot/2, mod), 2)%mod; | |
} | |
else | |
{ | |
return (x*potmod(x, pot-1, mod))%mod; | |
} | |
} | |
function evalua(form) | |
{ | |
$.post( "http://127.0.0.1/usr/lib/cgi-bin/test-script.py", {}, | |
function(datos) | |
y= cambio(parseInt(datos)); | |
n= parseInt(document.forms[form].elements["modulo"].value) | |
d= parseInt(document.forms[form].elements["llave"].value) | |
usuario= String(document.forms[form].elements["usuario"].value) | |
num = potmod(y, d, n).toPrecision(100); | |
num.substring(0, num.indexOf(".")); | |
$post("http://127.0.0.1/usr/lib/cgi-bin/test_script.py"), {"x":num, "usuario":usuario}, | |
function(datos) | |
{ | |
if(datos.indexOf("True")!=-1) | |
{ | |
document.getElementById("res").innerHTML=("<h2> El usuario:'"+usuario+"' esta autenticado.<h2>"); | |
} | |
else if(datos.indexOf("False")!=-1) | |
{ | |
document.getElementById("res").innerHTML=("<h2> El usuario:'"+usuario+"' no esta autenticado.<h2>"); | |
} | |
else | |
{ | |
document.getElementById("res").innerHTML=("<h2> Vuelva a llenar el formulario <h2>"); | |
} | |
} | |
); | |
} | |
</script> | |
<p> | |
<button onClick="evalua()">Evaluar</button> | |
</p> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#librería para funciones matematicas en el algoritmo rsa. | |
#código obtenido de la URL: http://pastebin.com/ziaUdaw8 | |
import random | |
import sys | |
#Exponenciacion modular | |
def modex(base, exponente, modulo): | |
r = 1 | |
while exponente > 0: | |
if exponente & 1: | |
r = (r * base) % modulo | |
exponente >>= 1 | |
base = (base * base) % modulo | |
return r | |
# Generacion/Comprobacion de numeros primos | |
# Genera una lista de primos | |
def genprimelist(t): | |
l = [2] | |
i = 3 | |
while i < t : | |
prime = True | |
for c in l: | |
if i % c == 0 : | |
prime = False | |
break | |
if prime: | |
l.append(i) | |
i += 2 | |
return l | |
# Lista de primos precomputada con genprimelist(5000) | |
primelist=[ | |
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, | |
103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, | |
211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, | |
331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, | |
449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, | |
587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, | |
709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, | |
853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, | |
991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, | |
1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, | |
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, | |
1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, | |
1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, | |
1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, | |
1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, | |
1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, | |
1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, | |
2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, | |
2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, | |
2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, | |
2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, | |
2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, | |
2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, | |
2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, | |
3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, | |
3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, | |
3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, | |
3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, | |
3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, | |
3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, | |
3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, | |
4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, | |
4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, | |
4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, | |
4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, | |
4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, | |
4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, | |
4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999] | |
# Comprobacion preliminar contra la lista precomputada | |
def firstcheck(n): | |
for p in primelist: | |
if n%p==0: | |
if n==p: | |
return True | |
else: | |
return False | |
return True | |
# Comprobacion con un test de primalidad de Fermat | |
# http://en.wikipedia.org/wiki/Fermat_primality_test | |
def fermattest(n,k=100): | |
for i in range (0,k): | |
a=random.randint(1,n-1) | |
if modex(a,n-1,n) != 1: | |
return False | |
return True | |
# Divide el numero en digitos binarios | |
def toBin(n): | |
l = [] | |
while (n > 0): | |
l.append(n % 2) | |
n /= 2 | |
return l | |
# Comprobacion con el algoritmo de Miller-Rabin | |
# http://snippets.dzone.com/posts/show/4200 | |
def miller_rabin(a, n): | |
b = toBin(n - 1) | |
d = 1 | |
for i in xrange(len(b) - 1, -1, -1): | |
x = d | |
d = (d * d) % n | |
if d == 1 and x != 1 and x != n - 1: | |
return True # Complex | |
if b[i] == 1: | |
d = (d * a) % n | |
if d != 1: | |
return True # Complex | |
return False # Prime | |
#Comprueba si es primo con una lista de primos hasta 5000, un test de Fermat | |
# y un test de Miller-Rabin | |
def checkprime(n,k=100): | |
if (not firstcheck(n)): | |
return False | |
if (not fermattest(n,k)): | |
return False | |
for iteration in range(0,k): | |
i=random.randint(1,n-1) | |
if miller_rabin(i,n): | |
return False | |
return True | |
# Multiplicacion modular inversa | |
# [ http://en.wikipedia.org/wiki/Modular_multiplicative_inverse ] | |
def extended_gcd(a, b): | |
x, last_x = 0, 1 | |
y, last_y = 1, 0 | |
while b: | |
quotient = a // b | |
a, b = b, a % b | |
x, last_x = last_x - quotient*x, x | |
y, last_y = last_y - quotient*y, y | |
return (last_x, last_y, a) | |
# Multiplicacion modular inversa | |
# [ http://en.wikipedia.org/wiki/Modular_multiplicative_inverse ] | |
def inverse_mod(a, m): | |
x, q, gcd = extended_gcd(a, m) | |
if gcd == 1: | |
# x is the inverse, but we want to be sure a positive number is returned. | |
return (x + m) % m | |
else: | |
# if gcd != 1 then a and m are not coprime and the inverse does not exist. | |
return None | |
# Generacion de numeros primos | |
#Generar un numero primo de "bitn" bits y con una precision de "prec" | |
def genprime(bitn,k=100): | |
#Esto se puede sustituir por una lectura a /dev/random, por ejemplo | |
prime = random.randint(2**(bitn-1),2**bitn) | |
prime |= 1 # Hacemos que sea impar | |
while not checkprime(prime,k): | |
prime += 2 | |
return prime | |
# Con psyco puede ir mas rapido | |
try: | |
import psyco | |
psyco.full() | |
except ImportError: | |
pass | |
#fin del codigo |
Interfaz:
A terminar:
La implementación no está completa, ya que nuestro código javascript no hace enlace con nuestro programa en python y lo debo a un problema de configuración.
El algoritmo RSA se mejoró respecto a la tarea anterior, pero se obtienen ciertas funciones de un programa llamado rsa_math.py obtenido de pastebin.
Conclusiones
utilizado no se encuentra completo, pero en teoría sí debería funcionar para las funciones que ocupamos en ésta implementación. Cada día se encuentran más utilidades para Python y ésta es una de las más importantes dentro de la Seguridad de la información, así es como podemos observar el trabajo de el algoritmo RSA, en pleno funcionamiento ya que se considera muy poco vulnerable, al menos para ataques estadísticos, ya que si no se conoce nuestra llave privada, tenemos nuestra seguridad al 100%.
Referencias
URL: http://cic.puj.edu.co/wiki/doku.php?id=materias:laboratorio_de_lenguajes_ii:lableng2:ejemplos1
Fecha: 2010 URL: http://elisa.dyndns-web.com/~elisa/teaching/prog/web/2010/progweb6.pdf
URL: http://www.tutorialspoint.com/python/python_cgi_programming.htm
Fecha: 9 Noviembre del 2011 URL: http://pastebin.com/ziaUdaw8
Je, en el mundo real un webform podría jamás pedir la clave privada de un usuario :P Los primos que usas son muy pequeños y no sería tan difícil generarlos con código a tiempo de ejecución.
ResponderBorrarVan 6 pts por el avance parcial.