Cifrado César#
Introducción#
En este capítulo vamos a presentar el cifrado César. Es uno de los métodos de cifrado más antiguos y conocidos en la criptografía. Recibe su nombre en honor al emperador romano Julio César, quien lo utilizaba para proteger mensajes militares confidenciales. Alrededor del año \(50\) antes de Cristo, Julio César, envió mensajes codificados a su general Marcus T. Cicero durante las Guerras de las Galias utilizando el cifrado César, el cual es un cifrado de sustitución basado en aritmética modular.
Veremos cómo cifrar un mensaje, es decir, que se convierta en un texto que no puede ser leído fácilmente. También veremos cómo descifrarlo y así descubrir cuál es el mensaje oculto.
Interpretar letras como números#
Antes de empezar, tendremos que dar algunas definiciones del vocabulario que usaremos.
Definición 49 (Definiciones)
Texto plano: el texto plano, también conocido como texto simple o texto sin formato, es una forma de representar información de manera legible por humanos sin ningún tipo de formato además, es el texto que queremos transmitir de forma secreta.
Texto cifrado: también conocido como criptograma o texto encriptado, es el resultado de aplicar un algoritmo de cifrado a un texto plano, éste es diseñado para ser ilegible para cualquier persona que no conozca la clave o el algoritmo utilizado para descifrarlo.
Cifrado: se refiere a un método o algoritmo utilizado para encriptar un texto plano, es decir, para transformar información legible en un formato incomprensible.
Descifrado: es el proceso inverso al cifrado, convierte el texto cifrado de vuelta a su forma original.
Lo que vamos a necesitar para el cifrado César es darle una interpretación numérica a las letras, esto nos será de mucha ayuda mas adelante para llevar a cabo el cifrado.
Vamos a trabajar con el alfabeto de \(27\) letras. A cada una le asignaremos un número. La siguiente tabla muestra qué valor se le asigna a cada una de las letras del alfabeto.
Observación 22
La idea de asignar una numeración a las letras del alfabeto es poder convertir un mensaje de texto plano en un mensaje de texto cifrado.
Respecto al texto plano, necesitamos acordar algunas reglas sobre como deben de ser escritos:
Los mensajes de texto plano sólo deben contener letras mayúsculas y espacios.
No debe haber ningún signo de puntuación o acentuaciones en los textos.
Veamos un ejemplo en el que mostraremos cómo se debería escribir un mensaje de texto plano y cómo se vería convertido en su forma numérica.
Ejemplo 152
Siguiendo las reglas establecidas, si quisiéramos escribir el mensaje
El alfabeto de \(27\) letras que decidimos usar en estas notas es por simplicidad, podrías agregar letras con acentos o puntuaciones, pero a medida que agregamos estos elementos al alfabeto, también estamos agregando mas números para identificarlos.
Usar un alfabeto sencillo hace que sea más fácil descifrar los mensajes. Mientras más caracteres agreguemos, más complicado será descifrar los mensajes, ya que la persona que quiera descifrar los mensajes debe de saber cuál es el alfabeto que se usó y de cuántos caracteres está compuesto.
Rotaciones en el alfabeto#
Ya vimos cómo un mensaje de texto plano puede convertirse en un mensaje de texto cifrado. Eso ya lo hace complicado de leer si no se sabe cómo se asignaron los números a cada letra del alfabeto. Ahora vamos a ver cómo complicar más la lectura de este mensaje.
Hablaremos sobre rotaciones en el abecedario, los cual es una técnica de cifrado simple. Consiste en desplazar cada letra del mensaje original un número fijo de posiciones hacia delante o hacia atrás en el alfabeto. Veamos un ejemplo para entender mejor las rotaciones. En la siguiente imagen mostramos una rotación del alfabeto.
En la imagen anterior el círculo exterior es el alfabeto original, el círculo interior es el alfabeto con una rotación en sentido a las manecillas del reloj. Usaremos la imagen para sustituir cada letra del círculo exterior por su correspondiente en el círculo interior. Por ejemplo, la letra \(A\) va a ser reemplazada por la letra \(D\), la \(B\) por la \(E\) y así con las demás letras.
Hay algo en particular que se debe de notar. Todas las letras del círculo exterior son sustituidas por una letra que está \(3\) posiciones adelante de ella misma. La letra \(A\) está en la posición \(1\) y la letra \(D\) está en la posición \(4\). Podría pensarse que el abecedario del círculo interior se rotó \(3\) posiciones en sentido de las manecillas del reloj. Esto es a lo que vamos a llamar rotación: mover el abecedario en sentido de las manecillas del reloj o en contra, tantas posiciones como queramos.
Notemos que en total hay \(27\) posibles rotaciones para hacer. Una de ellas es la rotación trivial en donde no se mueven la letras.
Ejemplo 153
Si usamos la rotación de \(3\) posiciones a la derecha entonces el mensaje
Si juntamos la interpretación de letras como números y las rotaciones en el alfabeto, tenemos lo necesario para utilizar el cifrado César.
Ahora, vamos a formalizar un poco la idea mediante congruencias.
Observación 24 (Cifrado César)
Sean \(P_1, P_2, \ldots , P_l\) el número que representa a las letras del texto plano y \(C_1, C_2, \ldots , C_l\) el valor numérico correspondiente en el texto cifrado. Entonces el cifrado César puede ser descrito por la congruencia
En el ejemplo Ejemplo 153 tendríamos que el valor de \(k\) sería \(3\) y en el Ejemplo 152 tendríamos un valor de \(k=0\). Ver el cifrado César con congruencias hace más sencillo el uso de este método.
Cifrar un mensaje#
Ya vimos cómo interpretar letras como números y también cómo hacer rotaciones en el abecedario. En los ejemplos anteriores sólo cambiamos las letras por números o letras por las letras de la rotación, pero el mensaje seguía con la misma estructura de las palabras. El cifrado César combina ambas herramientas, pero cuando cifremos mensajes una de las cosas que haremos será ignorar los espacios entre cada palabra. Además, vamos a agrupar las letras del texto cifrado de \(5\) en \(5\) para evitar que alguien descubra el patrón de las palabras. Esto complica aún más el cifrado. En caso de que el número de letras no sea múltiplo de \(5\), el último grupo de letras quedaría con una longitud entre \(1\) y \(4\). Ahora, vamos a juntar todo lo anterior y veamos en un ejemplo paso por paso cómo cifrar un mensaje en texto plano con el cifrado César.
Ejemplo 154
Vamos a cifrar el mensaje
Paso \(1\).
Necesitamos cambiar cada letra del mensaje en texto plano por su número correspondiente en Observación 22, es decir que tendríamos
Paso \(2\).
Vamos a aplicar la transformación usando la congruencia \(C_i\equiv P_i + k\pmod{27}\). Primero, vamos a elegir un valor para \(k\), tomemos \(k = 7\). Entonces a todos los valores \(P_i\) les vamos a sumar \(7\) y reducir módulo \(27\). Veamos un ejemplo con los primeros \(5\) valores, \(C_1, C_2, C_3, C_4\) y \(C_5\).
Entonces la trasformación aplicada a todos los valores sería,
Paso \(3\).
Necesitamos la tabla del abecedario con rotación \(7\), para sustituir los nuevos números por letras.

Por lo tanto el mensaje en texto cifrado sería
Como el mensaje aún parece estar partido en palabras, lo vamos a agrupar en grupos de \(5\), así no será tan obvio que hay palabras de \(2\) letras por ejemplo un «la» un «de» o algún otro monosílabo que sea fácil de reconocer. Por lo tanto, el mensaje final se vería como,
Código para cifrar un mensaje#
El siguiente código nos ayudará a convertir un texto plano en un texto cifrado usando el cifrado César, permitiéndonos elegir la rotación del alfabeto. El código recibe el mensaje en texto plano y la rotación, la cual debe ser un número entero entre \(0\) y \(26\).
def cifrado_cesar(texto, rotacion):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
texto_cifrado = ""
for letra in texto:
if letra in alfabeto:
indice = alfabeto.index(letra)
indice_cifrado = (indice + rotacion)%27
letra_cifrada = alfabeto[indice_cifrado]
texto_cifrado += letra_cifrada
texto_cifrado_f = [texto_cifrado[i:i+5] for i in range(0,len(texto_cifrado),5)]
return ' '.join(texto_cifrado_f)
texto_plano = "QUE LA FUERZA TE ACOMPAÑE"
mensaje_cifrado = cifrado_cesar(texto_plano, 7)
print("Mensaje original:", texto_plano)
print("Mensaje cifrado:", mensaje_cifrado)
Mensaje original: QUE LA FUERZA TE ACOMPAÑE
Mensaje cifrado: XBLRH MBLYG HALHJ VSWHU L
texto_plano = "LA TEORIA DE NUMEROS ES LA REINA DE LA MATEMATICA"
mensaje_cifrado = cifrado_cesar(texto_plano, 9)
print("Mensaje original:", texto_plano)
print("Mensaje cifrado:", mensaje_cifrado)
Mensaje original: LA TEORIA DE NUMEROS ES LA REINA DE LA MATEMATICA
Mensaje cifrado: TJCNX AQJMN VDUNA XBNBT JANQV JMNTJ UJCNU JCQLJ
Descifrar un mensaje conociendo la rotación#
Para descifrar un mensaje conociendo la rotación que se usó en el cifrado César, simplemente tenemos que despejar el valor de \(P\) en la congruencia que describe el cifrado César.
Observación 25 (Descifrar un mensaje)
Sean \(P_1, P_2, \ldots ,P_l\) el número que representa a las letras del texto plano y \(C_1, C_2, \ldots ,C_l\) su correspondiente en el texto cifrado. Entonces para descifrar un mensaje cifrado con César de rotación \(k\) usaríamos la congruencia
Veamos un ejemplo paso a paso para mostrar cómo descifrar un mensaje.
Ejemplo 155
Vamos a descifrar el siguiente mensaje que fue cifrado con César con una rotación de \(k=14\),
Paso \(1\).
Tenemos que cambiar las letras por su valor numérico dado en Observación 22. Entonces tendríamos:
Paso \(2\).
Ahora tenemos que usar la congruencia \(P_i\equiv C_i-k\pmod{27}\). Como sabemos la rotación, entonces tendríamos que usar \(P_i\equiv C_i-14\pmod{27}\).
Vamos a aplicar la transformación a manera de ejemplo a los primeros \(5\) valores, \(P_1, P_2, P_3, P_4\) y \(P_5\).
Así, la trasformación aplicada a todos los valores sería
Paso \(3\).
Ahora usamos la misma correspondencia en Observación 22 para regresar los valores a su correspondiente letra y así obtendríamos el mensaje original, es decir,
Luego, como ya es legible podemos separarla para una mejor lectura.
Código para descifrar el mensaje conociendo la rotación#
Podemos usar el mismo código creado en la sección anterior pero cambiando el nombre a las variables para que tenga más sentido. Además, como conocemos la rotación, para descifrar el mensaje el código aplica una rotación en sentido contrario a las manecillas del reloj, la cual se logra con un signo negativo.
def descifrado_cesar(texto, rotacion):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
texto_descifrado = ""
for letra in texto:
if letra in alfabeto:
indice = alfabeto.index(letra)
indice_descifrado = (indice - rotacion)%27
letra_descifrada = alfabeto[indice_descifrado]
texto_descifrado += letra_descifrada
return texto_descifrado
texto_plano = "XBLRH MBLYG HALHJ VSWHU L"
mensaje_descifrado = descifrado_cesar(texto_plano, 7)
print("Mensaje recibido:", texto_plano)
print("Mensaje descifrado:", mensaje_descifrado)
Mensaje recibido: XBLRH MBLYG HALHJ VSWHU L
Mensaje descifrado: QUELAFUERZATEACOMPAÑE
texto_plano = "YCGAI ZRFCG DFVZC GGCAY CGÑHC ZCGQR YÑÑFV HZRHV PÑ"
mensaje_descifrado = descifrado_cesar(texto_plano, 14)
print("Mensaje recibido:", texto_plano)
print("Mensaje descifrado:", mensaje_descifrado)
Mensaje recibido: YCGAI ZRFCG DFVZC GGCAY CGÑHC ZCGQR YÑÑFV HZRHV PÑ
Mensaje descifrado: LOSNUMEROSPRIMOSSONLOSATOMOSDELAARITMETICA
Descifrar un mensaje sin conocer la rotación#
Como ya hemos visto, el cifrado César es un cifrado de sustitución. Las letras originales del mensaje son sustituidas por otras haciendo el mensaje ilegible. Para romper este tipo de cifrados podemos hacer uso del criptoanálisis.
El criptoanálisis utiliza el conocimiento universalmente disponible de la distribución de frecuencias relativas de las letras en un texto ordinario, es decir, las letras que aparecen con mayor frecuencia en un texto dado. Es así que podemos fijarnos en la letras que más aparecen en el texto cifrado y suponer a cual corresponde en el texto plano.
Existen diferentes tablas de frecuencias de letras ya sea en idioma español o en cualquier otro idioma. No son únicas ya que cada tabla es calculada basada en diferentes textos. En este caso vamos a utilizar una tabla de frecuencias que se creó con el famoso libro de Don Quijote de la Mancha, el cual contiene un total de \(1,640,502\) letras.
Con la tabla anterior podríamos asumir que las letras más usadas en español son \(\text{e,a,o,s,n}\) y las menos usadas o que menos aparecen en un texto serían \(\text{k,w,x,ñ,z,f}\). Entonces, si tenemos un texto cifrado por César podemos suponer que la letra que más veces aparezca en el texto cifrado podría ser equivalente a alguna de las más frecuentes en el idioma español.
El siguiente ejemplo nos dará una idea de cómo podemos usar la tabla de frecuencia para descifrar el mensaje.
Ejemplo 156
Supongamos que recibimos el siguiente mensaje y sabemos que fue cifrado con el cifrado César.
Paso \(1\).
Tenemos que contar cuántas veces aparece cada letra en el texto. Si hacemos una tabla de frecuencia obtendríamos lo siguiente.
Paso \(2\).
Vamos a buscar la rotación.
Podemos observar que la letra \(\text{L}\) es la letra que más aparece en el texto cifrado, entonces nuestra mejor suposición sería que la letra \(\text{L}\) corresponde a la letra \(\text{E}\) en el texto plano.
Suponiendo que eso es verdadero, usaremos la tabla en Observación 22 para encontrar cuál fue la rotación. Sabemos que la letra \(\text{L}\) le corresponde el valor \(11\) y a la letra \(\text{E}\) le corresponde el valor \(04\), entonces tendríamos que
Entonces vamos a usar la congruencia \(P_i\equiv C_i - 7\pmod{27}\) para verificar si el mensaje es descifrado.
Paso \(3\).
Necesitamos cambiar el mensaje cifrado a valores numéricos y después aplicar la rotación para ver el mensaje resultante.
Ahora aplicamos la transformación \(P_i\equiv C_i - 7\pmod{27}\) a todos los valores y obtendríamos que
Entonces tendríamos el mensaje
Por lo tanto hemos encontrado que el mensaje es
Para terminar esta sección, hay que remarcar que las tablas de frecuencia no siempre funcionan al primer intento. Si no se descifra el mensaje entonces tenemos que ir probando con las siguientes letras más frecuentes para encontrar la rotación.
El método tiene algunas desventajas. Por ejemplo, si tuviéramos un texto en donde la letra más repetida no es de las primeras \(5\) más usadas en el idioma español entonces, nos llevaría mas intentos encontrar la rotación. Otro problema que puede ocurrir es que el texto a descifrar no sea lo suficientemente largo como para tener alguna coincidencia con la tabla de frecuencias. Mientras más largo sea el texto que queremos descifrar, mayor precisión tendrá este método.
Código para descifrar un mensaje sin conocer la rotación#
El siguiente código nos ayudará a descifrar un mensaje que fue cifrado con el cifrado César y no sabemos su rotación. Nos mostrará los primeros \(7\) intentos de descifrado.
def descifrar_sinrot(texto_cifrado):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
estadistica = "EAOSNRI"
diccionario = {}
for letra in texto_cifrado:
if letra in alfabeto:
if letra not in diccionario:
diccionario[letra] = 0
diccionario[letra] += 1
lista_frecuencias = sorted(diccionario.items(), key=lambda diccionario: diccionario[1], reverse=True)
ind_mas_frec = alfabeto.index(lista_frecuencias[0][0])
for i in estadistica:
ind_frecuencia = alfabeto.index(i)
rotacion = (ind_mas_frec - ind_frecuencia)%27
print(f"La rotación fue {rotacion} ")
print(descifrado_cesar(texto_cifrado,rotacion),"\n")
texto_cifrado = "RHJYO WAVNY HMOHL ZLRHY ALKLV JBRAH YSLTZ HPLZJ VTCOY AOLTK VWHRH IYHZL TJVKO NVZZL JYLAV ZXBLZ VRVWB LKLTZ LYLTA LTKOK VZWVY XBOLT LZAOL TLTRH JRHCL"
descifrar_sinrot(texto_cifrado)
La rotación fue 7
LACRIPTOGRAFIAESELARTEDEOCULTARMENSAJESCONVIRTIENDOPALABRASENCODIGOSSECRETOSQUESOLOPUEDENSERENTENDIDOSPORQUIENESTIENENLACLAVE
La rotación fue 11
HWYÑEMPLCÑWBEWAOAHWÑPAZALYQHPWÑIAJOWFAOYLJREÑPEAJZLMWHWXÑWOAJYLZECLOOAYÑAPLONQAOLHLMQAZAJOAÑAJPAJZEZLOMLÑNQEAJAOPEAJAJHWYHWRA
La rotación fue 23
VLNCSAEZQCLPSLODOVLCEOÑOZNFVELCWOXDLTODNZXGSCESOXÑZALVLMCLDOXNZÑSQZDDONCOEZDBFODZVZAFOÑOXDOCOXEOXÑSÑZDAZCBFSOXODESOXOXVLNVLGO
La rotación fue 19
ZOQGWEIDUGOTWOSHSZOGISRSDQJZIOGASBHOXSHQDBKWGIWSBRDEOZOPGOHSBQDRWUDHHSQGSIDHFJSHDZDEJSRSBHSGSBISBRWRDHEDGFJWSBSHIWSBSBZOQZOKS
La rotación fue 25
TJLAQYCXOAJÑQJNBNTJACNMNXLDTCJAUNVBJRNBLXVEQACQNVMXYJTJKAJBNVLXMQOXBBNLANCXBZDNBXTXYDNMNVBNANVCNVMQMXBYXAZDQNVNBCQNVNVTJLTJEN
La rotación fue 20
YÑPFVDHCTFÑSVÑRGRYÑFHRQRCPIYHÑFZRAGÑWRGPCAJVFHVRAQCDÑYÑOFÑGRAPCQVTCGGRPFRHCGEIRGCYCDIRQRAGRFRAHRAQVQCGDCFEIVRARGHVRARAYÑPYÑJR
La rotación fue 3
OEGVMTXSKVEJMEIWIOEVXIHISGYOXEVPIQWENIWGSQZMVXMIQHSTEOEFVEWIQGSHMKSWWIGVIXSWUYIWSOSTYIHIQWIVIQXIQHMHSWTSVUYMIQIWXMIQIQOEGOEZI
Nota que el siguiente ejemplo, la letra más frecuente en el texto cifrado es la segunda más frecuente en la nuestra tabla de frecuencias.
texto_cifrado = "TJCNX AQJMN VDUNA XBNBT JANQV JMNTJ UJCNU JCQLJ"
descifrar_sinrot(texto_cifrado)
La rotación fue 5
OEXISVMEHIQYPIVSWIWOEVIMQEHIOEPEXIPEXMGE
La rotación fue 9
LATEORIADENUMEROSESLAREINADELAMATEMATICA
La rotación fue 21
ZOISDGWORSBJASGDHSHZOGSWBORSZOAOISAOIWQO
La rotación fue 17
DSMWHKASVWFNEWKHLWLDSKWAFSVWDSESMWESMAUS
La rotación fue 23
XNGQBEUNPQZHYQEBFQFXNEQUZNPQXNYNGQYNGUON
La rotación fue 18
CRLVGJZRUVEMDVJGKVKCRJVZERUVCRDRLVDRLZTR
La rotación fue 1
SIBMWZPILMUCTMZWAMASIZMPUILMSITIBMTIBPKI
En el siguiente ejemplo, nuestro código no pudo descifrar el mensaje en los primeros \(7\) intentos.
texto_cifrado = "XBOGH ZPBGN HYSBR AOWRV ZMBLB AORWH YHJOM YHJOM YHZFR BPVZ"
descifrar_sinrot(texto_cifrado)
La rotación fue 24
AERJKCSEJPKBVEUDRZUYCOEÑEDRUZKBKMROBKMROBKCIUESYC
La rotación fue 1
WAÑFGYOAFMGXRAQZÑVQUYLAKAZÑQVGXGIÑLXGIÑLXGYEQAOUY
La rotación fue 13
LOCTUNDOTAUMGOFÑCKFJNZOYOÑCFKUMUWCZMUWCZMUNSFODJN
La rotación fue 9
OSGXYQHSXEYPKSJRGÑJNQDSCSRGJÑYPYAGDPYAGDPYQWJSHNQ
La rotación fue 15
JNARSLBNRYSKENDMAIDHLXNWNMADISKSUAXKSUAXKSLQDNBHL
La rotación fue 10
ÑRFWXPGRWDXOJRIQFNIMPCRBRQFINXOXZFCOXZFCOXPVIRGMP
La rotación fue 20
EIVNÑGWINTÑFZIYHVDYCGSIRIHVYDÑFÑPVSFÑPVSFÑGMYIWCG
Explicación del código#
En este capítulo vimos un método más para los diccionarios, el método items()
que al aplicarlo a un diccionario lo convierte en una lista de tuplas.
diccionario = {'carro_1':'rojo', 'carro_2':'verde', 'carro_3':'azul'}
lista = diccionario.items()
El resultado de la variable «lista» sería una lista como la siguiente \([(\text{'carro_1'},\text{'rojo'}), (\text{'carro_2'},\text{'verde'}), (\text{'carro_3'},\text{'azul'})]\). Las tuplas guardan las claves en la primera entrada y el valor en la segunda entrada.
Ahora vamos a analizar una instrucción mas detallada sobre la función sorted()
. Anteriormente usamos la función para ordenar de manera alfabética o numérica, en esta ocasión queremos ordenar una lista de tuplas respecto al segundo elemento en las tuplas.
El primer argumento de la función es un objeto iterable y como sabemos las listas son iterables.
El segundo argumento es key
y sirve para indicar cómo se debe comparar cada elemento para determinar el orden.
En este parámetro usamos una función anónima lambda diccionario: diccionario[1]
que toma una tupla de la lista y devuelve el segundo elemento de la tupla. Esta instrucción sirve para decirle a la función sorted()
que ordene la lista de tuplas basándose en el segundo valor de cada tupla.
El tercer y último argumento es reverse = True
, la función sorted()
ordena por default de menor a mayor. Este argumento nos ayuda a que nos ordene de forma inversa, es decir, empezando por el mayor o la última letra del alfabeto.
diccionario = {'carro_1':4, 'carro_2':2, 'carro_3':9}
valores_ordenados = sorted(diccionario.items(), key=lambda diccionario: diccionario[1], reverse=True)
El valor de la variable «valores_ordenados» es la lista de tuplas \([(\text{'carro_3'},9), (\text{'carro_1'},4), (\text{'carro_2'},2)]\).
También usamos el método join()
, este método nos sirve para que la impresión del mensaje cifrado sea mas clara. Comúnmente el método recibe como argumento una lista pero admite cualquier objeto iterable. Antes del método tenemos un espacio en blanco ' '
que sirve para indicarle al método el separador que usará entre los elementos de la lista.
lista = ["El","código","enigma"]
resultado = ' '.join(lista)
El resultado de la variable «resultado» es la cadena de caracteres \(\text{'El código enigma'}\).
Por último usamos un argumento más en la función range(valor inicial, valor final, salto)
, ya habíamos visto los primeros dos argumentos que nos daban un inicio y un final del intervalo que se creaba. Ahora introducimos un tercer argumento, lo que hace es saltar el números de valores que le indiquemos y no dar todo un rango completo. Por ejemplo, si pusiéramos como salto el valor \(5\), lo que nos arrojaría serían los valores \(0,5,10,15,20,25\ldots\), es decir, sólo múltiplos de \(5\).
Ahora vamos a explicar los códigos vistos.
Como observación, el código para cifrar y descifrar mensajes son casi idénticos lo único que cambia es la operación de sumar o restar la rotación y la forma de mostrar el resultado final.
Veamos el primer código, el cual usamos para cifrar mensajes.
def cifrado_cesar(texto, rotacion):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
texto_cifrado = ""
for letra in texto:
if letra in alfabeto:
indice = alfabeto.index(letra)
indice_cifrado = (indice + rotacion)%27
letra_cifrada = alfabeto[indice_cifrado]
texto_cifrado += letra_cifrada
texto_cifrado_f = [texto_cifrado[i:i+5] for i in range(0,len(texto_cifrado),5)]
return ' '.join(texto_cifrado_f)
Definimos una función llamada «cifrado_cesar» que recibe dos argumentos, el primer argumento es una cadena de texto el cual es conveniente guardarlo en una variable llamada «texto». El segundo argumento es la «rotacion» y es un número entero no negativo(entre \(0\) y \(26\)).
Luego definimos dos variables. La primera es «alfabeto» y es una cadena de texto que contiene todo el abecedario. La segunda es «texto_cifrado» y es una cadena de texto que sólo contiene un espacio.
def cifrado_cesar(texto, rotacion):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
texto_cifrado = ""
Ahora empezamos un ciclo
for
el cual recorre las letras de la variable «texto». Un condicionalif
revisa si el elemento que se tomó en el ciclofor
está en la cadena de texto de la variable «alfabeto», si esta condición es verdadera se reproduce el código dentro del condicional. Primero se calcula el valor numérico de la letra con la instrucciónalfabeto.index(letra)
y se guarda en la variable «indice». Luego hacemos la rotación, esto lo logramos con la instrucción(indice + rotacion)%27
y lo guardamos en la variable «indice_cifrado», así es como logramos mover el índice de la letra original tantos espacios como queramos. Ahora tenemos que volver a convertir el valor numérico a su nueva letra cifrada. Esto lo logramos con la instrucciónalfabeto[indice_cifrado]
que se guarda en la variable «letra_cifrada». Por último, este valor se agrega a la variable «texto_cifrado». Como ambos valores son de tipostr
podemos sumarlos y obtener nuevamente una cadena de texto.
Este ciclofor
se repite para todas las letras en el texto que introduzcamos y así obtenemos el texto cifrado.
for letra in texto:
if letra in alfabeto:
indice = alfabeto.index(letra)
indice_cifrado = (indice + rotacion)%27
letra_cifrada = alfabeto[indice_cifrado]
texto_cifrado += letra_cifrada
Esta última parte del código solo nos sirve para presentar el resultado. Lo que hacemos es una comprensión de listas que toma letras de la variable «texto_cifrado» y va formando elementos de \(5\) letras para guardarlos en la lista. Finalmente usamos el método
join()
para tomar las palabras de la lista y unirlas con un espacio para así formar el mensaje cifrado.
texto_cifrado_f = [texto_cifrado[i:i+5] for i in range(0,len(texto_cifrado),5)]
return ' '.join(texto_cifrado_f)
Para presentar el resultado creamos la variable «texto_plano» la cual tiene como valor una cadena de texto. Luego usamos la función creada «cifrado_cesar», pasando como argumentos la variable «texto_plano» y un entero positivo para la rotación. Para finalizar, usamos la función
print()
para presentar el texto original y el texto cifrado.
texto_plano = "QUE LA FUERZA TE ACOMPAÑE"
mensaje_cifrado = cifrado_cesar(texto_plano, 7)
print("Mensaje original:", texto_plano)
print("Mensaje cifrado:", mensaje_cifrado)
Veamos el código para descifrar mensajes.
def descifrado_cesar(texto, rotacion):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
texto_descifrado = ""
for letra in texto:
if letra in alfabeto:
indice = alfabeto.index(letra)
indice_descifrado = (indice - rotacion)%27
letra_descifrada = alfabeto[indice_descifrado]
texto_descifrado += letra_descifrada
return texto_descifrado
Como mencionamos es muy parecido al código para cifrar mensajes. Dentro del código lo único que hay diferente es la variable «indice_descifrado», la cual se calcula con la instrucción (indice - rotacion)%27
, es decir que la diferencia con el otro código es la resta de la rotación.
La otra diferencia es la salida del mensaje. En esta ocasión lo que nos imprime la consola son todas las letras juntas sin espacios. Este se presenta así porque para que Python pueda interpretar palabras y separarlas necesita otro tipo de herramientas que no veremos aquí.
Por último, veamos el código para descifrar mensajes sin conocer la rotación.
def descifrar_sinrot(texto_cifrado):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
estadistica = "EAOSNRI"
diccionario = {}
for letra in texto_cifrado:
if letra in alfabeto:
if letra not in diccionario:
diccionario[letra] = 0
diccionario[letra] += 1
lista_frecuencias = sorted(diccionario.items(), key=lambda diccionario: diccionario[1], reverse=True)
ind_mas_frec = alfabeto.index(lista_frecuencias[0][0])
for i in estadistica:
ind_frecuencia = alfabeto.index(i)
rotacion = (ind_mas_frec - ind_frecuencia)%27
print(f"La rotación fue {rotacion} ")
print(descifrado_cesar(texto_cifrado,rotacion),"\n")
Definimos una función llamada «descifrar_sinrot» que recibe como argumento una cadena de texto la cual es conveniente guardarla en una variable llamada «texto_cifrado».
Después creamos dos variables. La primer variable es «alfabeto» la cual contiene una cadena de texto que es todo el abecedario. La segunda variable es «estadistica» la cual contiene una cadena de texto con las \(7\) letras usadas con más frecuencia en un texto en español.
def descifrar_sinrot(texto_cifrado):
alfabeto = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"
estadistica = "EAOSNRI"
Creamos un diccionario vacío.
Luego comenzamos un ciclofor
el cual recorre las letras de la variable «texto_cifrado». En un condicionalif
revisamos si la letra se encuentra en la variable «alfabeto», de ser cierto se reproduce el código dentro.
Con otro condicionalif
revisamos si ya existe un objeto dentro del diccionario con la clave «letra», si no existe entonces se crea el objeto dentro del diccionario \(\{\text{'letra'}:0 \}\), con un valor inicial de \(0\). Si ya existe el objeto, entonces el código salta a la siguiente línea la cual actualiza el valor del objeto dentro del diccionario con la clave «letra» y suma una unidad a su valor.
El ciclo se repite hasta recorrer todas las letras, y así obtener el conteo de cuántas veces aparece cada letra en el texto.
diccionario = {}
for letra in texto_cifrado:
if letra in alfabeto:
if letra not in diccionario:
diccionario[letra] = 0
diccionario[letra] += 1
Luego con el método
items()
obtenemos la lista de tuplas \((\text{'clave'},\text{'valor'})\) del diccionario y usamos la funciónsorted()
para obtener la lista ordenada de mayor a menor que guardamos en la variable «lista_frecuencias». Así logramos obtener una lista con las letras y su frecuencia ordenadas de mayor a menor. Luego en la variable «ind_mas_frec» guardamos la letra mas frecuente.
lista_frecuencias = sorted(diccionario.items(), key=lambda diccionario: diccionario[1], reverse=True)
ind_mas_frec = alfabeto.index(lista_frecuencias[0][0])
Por último creamos un ciclo
for
en el cual recorremos las letras dentro de la variable «estadistica». Obtenemos el valor numérico del elemento tomado por el ciclofor
con la instrucciónalfabeto.index()
el cual lo guardamos en la variable «ind_frecuencia». Luego calculamos la rotación usando la instrucción(ind_mas_frec - ind_frecuencia)%27
, este se guarda en la variable «rotacion». Finalmente, una vez obtenida la rotación el código imprime en pantalla cuál fue la rotación encontrada y usamos la función «descifrado_cesar» para obtener el mensaje descifrado. Este ciclo solo se repite \(7\) veces con las \(7\) letras más frecuentes.
Podría modificarse el código para realizar las \(26\) rotaciones en caso de que con las primeras \(7\) no se descifre el mensaje.
for i in estadistica:
ind_frecuencia = alfabeto.index(i)
rotacion = (ind_mas_frec - ind_frecuencia)%27
print(f"La rotación fue {rotacion} ")
print(descifrado_cesar(texto_cifrado,rotacion),"\n")
Ejercicios de práctica#
Los siguientes mensajes fueron encriptados con el cifrado César, descifra el mensaje.
\(\text{"BQMYT QUJBF HLUJL SUTUC YUDKI QJUJK QJFSL GQTFX QSYUD TFFKI FJGBQ DUJ."}\)
\(\text{"ZOSME ZDGOQ WDBSH EOQWO ZVOSM EOBRW RDBJS HIGDH VDGWÑ DBISH NBDHV OESGA WIWRD QDAEG SBRSG ASXDG BJSHI GDZJU OGSBS ZJBWK SGHD."}\)
\(\text{"RHJOI LYZLN BYOKH KLZJY BJOHR WHYHW YVALN LYTBL ZAYVZ KHAVZ LTBTS BTKVJ HKHCL GSHZJ VTLJA HKV."}\)
Cifra el siguiente mensaje utilizando la congruencia \(C\equiv 5P + 11\pmod{27}\).
\(\text{"LA BUSQUEDA DE LA FELICIDAD ES UN VIAJE ETERNO NO UN DESTINO FIJO."}\)
Supongamos que un mensaje fue cifrado con una congruencia de la forma \(C\equiv aP + k\pmod{27}\). Si el valor de \(k = 15\).
¿Puedes descifrar el mensaje \(\text{"PLCQW GOJMC PNOGP NNMLM AYMJP RACKM NRPTM JMN"}\)?
Pista: Utiliza el método de criptoanálisis.
Haz una tabla de frecuencias de las letras del mensaje cifrado.
Haz la suposición de que la letra mas frecuente corresponde a alguna en el texto plano.
Encuentra el valor de \(a\).
Verifica si el mensaje descifrado tiene sentido con el valor encontrado, si no repite el procedimiento.
Supongamos que un mensaje fue cifrado con una congruencia de la forma \(C\equiv aP + k\pmod{27}\).
¿Puedes descifrar el mensaje \(\text{"THUXP AÑALX OCÑHX AXTCH EÑOKD HCÑHX TXBÑL K".}\)
Pista: Utiliza el método de criptoanálisis.
Haz una tabla de frecuencias de las letras del mensaje cifrado.
Haz dos suposiciones de dos letras diferentes. Por ejemplo que \(C_1\) sea correspondiente a \(P_1\) y que \(C_2\) sea correspondiente a \(P_2\).
Forma el sistema de congruencias y resuelvelo.
\[\begin{align*} C_1 &\equiv aP_1 + k\pmod{27} \\ & \\ C_2 &\equiv aP_2 + k\pmod{27}. \end{align*}\]Una vez encontrados \(a\) y \(k\) verifica si el mensaje descifrado tiene sentido.
Descifra