Sage Worksheet: RSA http://localhost:8000/home/admin/37/print RSA Encriptación asimétrica con RSA Codificar un mensaje de texto en números Antes de poder aplicar el método RSA, tenemos que codificar la información que queremos mandar como una secuencia de números enteros. El código siguiente hace dos cosas: Sustituye cada carácter por su código numérico. Agrupa un bloque de varios números en un sólo número más grande. #nuestro alfabeto son el espacio en blanco y las mayusculas sin acentos alfabeto=' ABCDEFGHIJKLMNOPQRSTUVWXYZ' L=len(alfabeto) def codifica_letra(letra): return alfabeto.index(letra) def bloque2numero(bloque): b=len(bloque) return sum(n*L^(b-1-i) for i,n in enumerate(bloque)) def codifica_mensaje(mensaje,b): '''Convierte una secuencia de letras en una secuencia de numeros Las letras validas son las mayusculas y el espacio en blanco El espacio en blanco se codifica como 0, las letras a partir de 1 Las letras se agrupan en bloques de b letras ''' #cambiamos las letras por sus codigos letras = [codifica_letra(letra) for letra in mensaje] #rellenamos con espacios en blanco al final letras = letras + [0]*(b-len(letras)%b) n = len(letras) #Agrupamos en bloques bloques = [letras[i:i+b] for i in range(0,n,b)] #cambiamos cada bloque por un numero codigo = [bloque2numero(bloque) for bloque in bloques] return codigo mensaje='CITA EN EL PATIO' 1 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print letras = [codifica_letra(letra) for letra in mensaje] print letras [3, 9, 20, 1, 0, 5, 14, 0, 5, 12, 0, 16, 1, 20, 9, 15] #Agrupamos las letras b=3 n = len(letras) bloques = [letras[i:i+b] for i in range(0,n,b)] print bloques [[3, 9, 20], [1, 0, 5], [14, 0, 5], [12, 0, 16], [1, 20, 9], [15]] codigo = [bloque2numero(bloque) for bloque in bloques] print codigo [2450, 734, 10211, 8764, 1278, 15] mensaje='CITA EN EL PATIO' codifica_mensaje(mensaje,2) [90, 541, 5, 378, 147, 16, 47, 258, 0] Las operaciones inversas son similares, ahora tenemos que recuperar el texto a partir de la secuencia de números. def decodifica_letra(n): return alfabeto[n] def numero2bloque(n,b): bloque=[] for i in range(b): bloque.append(n%L) n=n//L bloque.reverse() return bloque def decodifica_mensaje(secuencia,b): '''Convierte una secuencia de numeros en una secuencia de letras ''' bloques=[numero2bloque(numero,b) for numero in secuencia] mensaje=''.join(''.join(decodifica_letra(letra) for letra in bloque) for bloque in bloques) return mensaje decodifica_mensaje([90, 541, 5, 378, 147, 16, 47, 258],2) 'CITA EN EL PATIO' Encriptación RSA 2 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print En el sistema RSA, un número menor que N (que puede representar texto, o cualquier otra cosa), se encripta elevándolo a un exponente módulo N. La operación de desencriptado también usa la misma operación, pero con un exponente distinto. Aunque podríamos usar la misma función para las tareas de encriptar y desencriptar, preferimos usar dos funciones distintas por claridad. Encriptado Cada número de la secuencia se eleva al exponente e módulo N. Por tanto, para encriptar se necesita el par formado por N y e, que llamaremos la clave pública. x ! xe (mod N ) Desencriptado Cada número de la secuencia se eleva al exponente d módulo N. Para desencriptar se necesita el par formado por N y d, que llamaremos la clave privada. y ! y d(mod N ) def encripta_RSA(lista,N,e): '''Encripta una secuencia de numeros siguiendo el metodo RSA''' return [power_mod(numero,e,N) for numero in lista] def desencripta_RSA(lista,N,d): '''Desencripta una secuencia de numeros siguiendo el metodo RSA''' return [power_mod(numero,d,N) for numero in lista] Uniendo los pasos de codificar un texto y encriptarlo, componemos estas dos funciones que trabajan directamente con una cadena de caracteres y una clave RSA. def encripta_mensaje(mensaje, clave_publica): '''Encripta una cadena de texto siguiendo el metodo RSA clave_publica es la tupla formada por N y e ''' N,e=clave_publica b=floor( log(N)/log(L) ) if b<1: raise Exception("N es demasiado pequeño") mensaje_codificado = codifica_mensaje(mensaje,b) mensaje_encriptado = encripta_RSA(mensaje_codificado,N,e) return mensaje_encriptado 3 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print def desencripta_mensaje(secuencia, clave_privada): '''Desencripta una cadena de texto siguiendo el metodo RSA clave_privada es la tupla formada por N y d ''' N,d=clave_privada b=floor( log(N)/log(L) ) if b<1: raise Exception("N es demasiado pequenyo") mensaje_codificado = desencripta_RSA(secuencia,N,d) mensaje_decodificado = decodifica_mensaje(mensaje_codificado,b) return mensaje_decodificado Un ejemplo con números pequeños Para que las operaciones de encriptado y desencriptado sean inversas una de la otra, se tiene que verificar, para cualquier x: xde = x(modN ) Los números siguientes tienen esta propiedad, así que al desencriptar deberíamos recuperar el mensaje original. El número N es el producto de dos números primos p y q. p=29; q=31; N=p*q e=13 d=517 clave_publica=(N,e) mensaje_encriptado=encripta_mensaje(mensaje, clave_publica) print mensaje_encriptado [193, 90, 470, 378, 449, 252, 66, 474, 0] factor(N) 29 * 31 clave_privada=(N,d) desencripta_mensaje(mensaje_encriptado,clave_privada) 'CITA EN EL PATIO ' Generar pares de clave pública y privada Veamos ahora cómo encontrar pares de clave pública y privada arbitrariamente grandes. Necesitamos números N, e y d tales que xde = x(mod N ) 4 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print para cualquier 1 < x < N , pero además no debe ser fácil encontrar d a partir de e: Tomamos N igual al producto de dos primos muy grandes. Buscamos e que sea primo con ¶(N ) = (p À 1)(q À 1) . Gracias al paso anterior, existe el número d, inverso de e módulo ¶(N ). Gracias al teorema de Euler : xde = x(mod N ) para cualquier x. #Generar aleatoriamente dos primos grandes tamanyo = 1e30 K=randint(tamanyo,2*tamanyo) p=next_prime(K) K=randint(tamanyo,2*tamanyo) q=next_prime(K) N=p*q print p,q,N 1399202587512894772741643296763 1216342295148791908885703216473 1701909286673562793883932669753044153846238826378628769176899 Escoge un exponente e que sea invertible módulo ¶(N ) = (p À 1)(q À 1) : phi=(p-1)*(q-1) e=randint(1,phi) while gcd(e,phi)>1: e=randint(1,phi) print e 102875427855504315802629514980602362595287921214553701444035 Invierte el exponente e módulo ¶(N ): d=inverse_mod(e,phi) print d 1659190621842085354211332802738720692710641884518333722456347 Comprobamos que todo ha salido bien encriptando un mensaje y luego desencriptándolo. Usamos un alfabeto más grande para poder encriptar parte del manifiesto del HM2009. alfabeto = u''' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:; <=>?@[\\]^_`{|}~\t\n\x0b\x0c\r0123456789áéíóúñ''' L=len(alfabeto) manifiesto=u'''Manifiesto 2009 - "NoMadHack" 5 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print Indagando en los silenciosos mares de silicio, recodificando colaborativamente, y atentos a los murmullos en los canales de fibra óptica, donde la información nunca deja de fluir buscando desesperadamente un destino, hemos descubierto este manifiesto, escrito anónimamente por la sociedad mundial, en nuestra red de recogida de inquietudes, intenciones y sentimientos virtuales. ''' numeros=encripta_mensaje(manifiesto,(N,e)) print numeros [767316439615146371263305744853750774577582631295915800741956, 1219129321596790998293032220846359076621512734359825062971210, 280006809176213994423866324574893407907620540695819163652920, 1275280816142767422618827715652261635483641212174084914258700, 1526872740037813977695373545463045539919157447453346711620489, 490022820801324549455506420399127484501466203952632736054027, 1530272408594618934255992425726190139939304650965964768052996, 490663447710086876866707489615911175147870842855793504566942, 1612577536193522153445903930841898873535305421574988348094717, 803527972039880827254988267393546225501447892951356939858473, 153246573635015736579341055287833208329462505933441860942187, 1660653068916384763215983321454884533885671773483351735720081, 651069541605655122898174381968949152680290158035301568935997, 1293284215516668583734623532290275165390774878096694261752438, 1384785068528417481118056455729506735458293146739727140277600] #Deberiamos recuperar el mensaje original, aunque los #caracteres unicode se ven un poco distintos desencripta_mensaje(numeros,(N,d)) u'Manifiesto 2009 - "NoMadHack"\n\nIndagando en los silenciosos mares de silicio, recodificando colaborativamente, y atentos a los murmullos en los canales de fibra \xf3ptica, donde la informaci\xf3n nunca deja de fluir buscando desesperadamente un destino, hemos descubierto este manifiesto, escrito an\xf3nimamente por la sociedad mundial, en nuestra red de recogida de inquietudes, intenciones y sentimientos virtuales.\n ' La seguridad del sistema RSA se basa en que calcular la clave privada en función de la clave pública requiere mucho tiempo de cómputo. Pero si conocemos la factorización de N, conocemos el número '(N ), y podemos calcular el exponente de desencriptado. El pilar del sistema es que factorizar números grandes lleva mucho más tiempo de cómputo que encontrar números primos grandes. Si dedicamos un poco más de tiempo a buscar números primos un poco más grandes, el tiempo necesario 6 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print para factorizar el producto de los números aumenta en una proporción mucho mayor. %time tamanyo = 1e10 K=randint(tamanyo,2*tamanyo) p=next_prime(K) K=randint(tamanyo,2*tamanyo) q=next_prime(K) N=p*q print p,q,N 19334855567 16460029739 318252297632089707013 CPU time: 0.00 s, Wall time: 0.00 s %time factor(N) 16460029739 * 19334855567 CPU time: 0.05 s, Wall time: 0.06 s %time tamanyo = 1e20 K=randint(tamanyo,2*tamanyo) p=next_prime(K) K=randint(tamanyo,2*tamanyo) q=next_prime(K) N=p*q print p,q,N 150396791728406549917 194090517350253292399 29190591114384722328697921011425790180883 CPU time: 0.03 s, Wall time: 0.03 s %time factor(N) 150396791728406549917 * 194090517350253292399 CPU time: 0.86 s, Wall time: 0.97 s %time tamanyo = 1e25 K=randint(tamanyo,2*tamanyo) p=next_prime(K) K=randint(tamanyo,2*tamanyo) q=next_prime(K) 7 de 8 11/10/09 22:01 Sage Worksheet: RSA http://localhost:8000/home/admin/37/print N=p*q print p,q,N 15425007669588100835266369 12991625517216356000447393 200395923243478768657278016592972692728356326626017 CPU time: 0.08 s, Wall time: 0.09 s %time factor(N) 12991625517216356000447393 * 15425007669588100835266369 CPU time: 9.95 s, Wall time: 10.64 s 8 de 8 11/10/09 22:01