paint-brush
Cifrado: cifras, resúmenes, sal e IV: lo que necesita saberpor@gliimlang
Nueva Historia

Cifrado: cifras, resúmenes, sal e IV: lo que necesita saber

por Gliimly14m2025/01/04
Read on Terminal Reader

Demasiado Largo; Para Leer

El cifrado es un método que convierte los datos en algo inutilizable que solo puede volverse útil mediante el descifrado. Aquí aprenderá a cifrar y descifrar datos mediante una contraseña.
featured image - Cifrado: cifras, resúmenes, sal e IV: lo que necesita saber
Gliimly HackerNoon profile picture

¿Qué es el cifrado?

El cifrado es un método para convertir los datos en algo inutilizable que solo se puede volver útil mediante el descifrado. El objetivo es que los datos estén disponibles únicamente para aquellos que puedan descifrarlos (es decir, hacerlos utilizables). Por lo general, los datos deben cifrarse para asegurarse de que no se puedan obtener en caso de acceso no autorizado. Es la última línea de defensa después de que un atacante haya logrado atravesar los sistemas de autorización y control de acceso.

Esto no significa que todos los datos deban estar cifrados, ya que, a menudo, los sistemas de autorización y acceso pueden ser suficientes y, además, existe una penalización de rendimiento por cifrar y descifrar los datos. Si los datos se cifran y cuándo se cifran es una cuestión de planificación de la aplicación y evaluación de riesgos y, a veces, también es un requisito normativo, como en el caso de la HIPAA o el RGPD.

Los datos se pueden cifrar en reposo, como en un disco, o en tránsito, como entre dos partes que se comunican a través de Internet.

Aquí aprenderá a cifrar y descifrar datos mediante una contraseña, también conocida como cifrado simétrico. Esta contraseña debe ser conocida por ambas partes que intercambian información.

Cifrado, Compendio, Sal, Iteraciones, IV

Para utilizar el cifrado de forma adecuada y segura, hay algunas nociones que es necesario explicar.


Un cifrado es el algoritmo que se utiliza para el cifrado. Por ejemplo, AES256 es un cifrado. La idea de un cifrado es lo que la mayoría de las personas piensan cuando se trata de cifrado.


Un resumen es básicamente una función hash que se utiliza para codificar y alargar la contraseña (es decir, la clave de cifrado) antes de que la utilice el sistema de cifrado. ¿Por qué se hace esto? Por un lado, crea un hash de longitud uniforme y bien aleatorizado de una clave que funciona mejor para el cifrado. También es muy adecuado para el "salting", que es el siguiente tema del que hablaremos.


La "sal" es un método para derrotar a las llamadas tablas "arcoíris". Un atacante sabe que dos valores hash seguirán luciendo exactamente iguales si los originales lo fueran. Sin embargo, si agrega el valor de sal al hash, entonces no lo harán. Se llama "sal" porque está mezclado con la clave para producir algo diferente.


Ahora, una tabla arcoíris intentará hacer coincidir valores hash conocidos con datos precalculados en un intento de adivinar una contraseña.


Por lo general, la sal se genera aleatoriamente para cada clave y se almacena con ella. Para que coincida con los hashes conocidos, el atacante tendría que calcular previamente tablas arcoíris para una gran cantidad de valores aleatorios, lo que generalmente no es factible.

A menudo se oye hablar de "iteraciones" en el cifrado. Una iteración es un ciclo único en el que se mezclan una clave y una sal de forma que resulte más difícil adivinar la clave. Esto se hace muchas veces para que a un atacante le resulte difícil desde el punto de vista computacional adivinar la clave a la inversa, de ahí el término "iteraciones" (en plural). Normalmente, el número mínimo de iteraciones necesario es de 1000, pero puede ser diferente. Si se empieza con una contraseña muy segura, normalmente se necesitan menos.

IV (o "Vector de inicialización") es típicamente un valor aleatorio que se utiliza para el cifrado de cada mensaje. Ahora, salt se utiliza para producir una clave basada en una contraseña. Y IV se utiliza cuando ya tienes una clave y ahora estás cifrando mensajes. El propósito de IV es hacer que los mismos mensajes aparezcan de manera diferente cuando se cifran. A veces, IV también tiene un componente secuencial, por lo que está hecho de una cadena aleatoria más una secuencia que aumenta constantemente.


Esto dificulta los ataques de "reproducción", en los que el atacante no necesita descifrar un mensaje; en lugar de ello, un mensaje cifrado se "captura" (es decir, se intercepta entre el remitente y el receptor) y luego se reproduce, con la esperanza de repetir la acción ya realizada. Aunque en realidad, la mayoría de los protocolos de alto nivel ya tienen una secuencia establecida, donde cada mensaje tiene, como parte de él, un número creciente de paquetes, por lo que en la mayoría de los casos, IV no lo necesita.

Prerrequisitos

Este ejemplo utiliza el framework Gliimly . Instálelo primero.

Ejemplo de cifrado

Para ejecutar los ejemplos aquí, cree una aplicación "enc" en un directorio propio (consulte mgrg para obtener más información sobre el administrador de programas de Gliimly):

 mkdir enc_example cd enc_example gg -k enc

Para cifrar datos, utilice la instrucción encrypt-data . La forma más sencilla es cifrar una cadena terminada en cero. Cree un archivo "encrypt.gliim" y copie esto:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str p-out dec_str @ end-handler

Puedes ver el uso básico de encrypt-data y decrypt-data . Proporcionas los datos (originales o encriptados) y la contraseña, y listo. Los datos se encriptan y luego se desencriptan, obteniendo el original.

En el código fuente, una variable de cadena "enc_str" (que se crea como "char *") contendrá la versión cifrada de "¡Esto contiene un código secreto, que es Ábrete Sésamo!" y "dec_str" serán los datos descifrados que deben ser exactamente los mismos.

Para ejecutar este código desde la línea de comandos, primero cree la aplicación:

 gg -q

Luego, haga que Gliimly produzca el código bash para ejecutarlo: la ruta de la solicitud es "/encrypt", que, en nuestro caso, se maneja mediante la función "void encrypt()" definida en el archivo fuente "encrypt.gliim". En Gliimly, estos nombres siempre coinciden, lo que facilita la escritura, lectura y ejecución del código. Use la opción "-r" en gg para especificar la ruta de la solicitud y obtener el código que necesita para ejecutar el programa:

 gg -r --req="/encrypt" --silent-header --exec


Recibirás una respuesta como esta:

 72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9 This contains a secret code, which is Open Sesame!


Lo que tienes aquí son los datos cifrados, que luego se descifran utilizando la misma contraseña. Como era de esperar, el resultado coincide con la cadena que cifraste en primer lugar.

Tenga en cuenta que, de forma predeterminada, encrypt-data producirá un valor cifrado en formato hexadecimal legible para humanos, lo que significa que consta de caracteres hexadecimales del "0" al "9" y de la "a" a la "f". De esta manera, puede almacenar los datos cifrados en una cadena normal. Por ejemplo, puede ir a un documento JSON, a una columna VARCHAR en una base de datos o prácticamente a cualquier otro lugar. Sin embargo, también puede producir datos cifrados binarios. Más sobre eso en un momento.

Cifrar datos en un resultado binario

En el ejemplo anterior, los datos cifrados resultantes están en formato hexadecimal legible para humanos. También puedes crear datos cifrados binarios, que no son cadenas legibles para humanos y también son más cortos. Para ello, utiliza la cláusula "binary". Reemplaza el código en "encrypt.gliim" por:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" binary // Save the encrypted data to a file write-file "encrypted_data" from enc_str get-app directory to app_dir @Encrypted data written to file <<p-out app_dir>>/encrypted_data // Decrypt data decrypt-data enc_str password "my_password" binary to dec_str p-out dec_str @ end-handler

Cuando desee obtener datos cifrados binarios, también debe obtener su longitud en bytes; de lo contrario, no sabrá dónde termina, ya que puede contener bytes nulos. Utilice la cláusula "output-length" para ese propósito. En este código, los datos cifrados en la variable "enc_str" se escriben en el archivo "encrypted_data" y la longitud escrita es "outlen" bytes.


Cuando se escribe un archivo sin una ruta, siempre se escribe en el directorio de inicio de la aplicación (ver directorios ), por lo que deberá usar get-app para obtener ese directorio.

Al descifrar datos, observe el uso de la cláusula "input-length". Indica cuántos bytes tienen los datos cifrados. Obviamente, puede obtener eso de la variable "outlen", donde encrypt-data almacena la longitud de los datos cifrados. Cuando el cifrado y el descifrado están desacoplados, es decir, se ejecutan en programas separados, se asegura de que esta longitud esté disponible.

Tenga en cuenta también que cuando los datos se cifran como "binarios" (lo que significa que producen una salida binaria), el descifrado debe utilizar el mismo.

Realizar la solicitud:

 gg -q


Ejecútalo igual que antes:

 gg -r --req="/encrypt" --silent-header --exec


El resultado es:

 Encrypted data written to file /var/lib/gg/enc/app/encrypted_data This contains a secret code, which is Open Sesame!


Los datos descifrados son exactamente los mismos que los originales.

Puede ver los datos cifrados reales escritos en el archivo utilizando la utilidad de Linux "octal dump" ("od"):

 od -c /var/lib/gg/enc/app/encrypted_data


con el resultado como:

 $ od -c /var/lib/gg/enc/app/encrypted_data 0000000 r 335 324 L 020 351 i ; 346 254 w 312 253 306 N 005 0000020 370 \t ) \n 020 235 367 317 305 t \0 224 214 270 210 315 0000040 # 307 351 216 025 274 362 033 % 253 023 7 335 306 320 0000060 224 # ! 021 252 242 325 H 300 217 # \vm V 351 0000100

Ahí lo tienes. Notarás que los datos son binarios y que, en realidad, contienen bytes nulos.

Cifrar datos binarios

Los datos que se van a cifrar en estos ejemplos son cadenas, es decir, delimitadas por valores nulos. Puedes cifrar datos binarios con la misma facilidad, especificándolos enteros (¡ya que Gliimly lleva un registro de cuántos bytes hay!) o especificando su longitud en la cláusula "input-length", por ejemplo, copia esto en "encrypt.gliim":

 begin-handler /encrypt public set-string str = "This c\000ontains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" input-length 12 p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str // Output binary data; present null byte as octal \000 string-length dec_str to res_len start-loop repeat res_len use i start-with 0 if-true dec_str[i] equal 0 p-out "\\000" else-if pf-out "%c", dec_str[i] end-if end-loop @ end-handler

Esto cifrará 12 bytes en la ubicación de memoria "enc_str" independientemente de los bytes nulos. En este caso, se trata de "This c" seguido de un byte nulo seguido de una cadena "ontain", pero puede ser cualquier tipo de datos binarios, por ejemplo, el contenido de un archivo JPG.

En el lado del descifrado, se obtiene la cantidad de bytes descifrados en la cláusula "output-length". Finalmente, se muestra que los datos descifrados son exactamente los originales y el byte nulo se presenta en una representación octal típica.

Realizar la solicitud:

 gg -q


Ejecútalo igual que antes:

 gg -r --req="/encrypt" --silent-header --exec


El resultado es:

 6bea45c2f901c0913c87fccb9b347d0a This c\000ontai


El valor cifrado es más corto porque los datos también son más cortos en este caso y el resultado coincide exactamente con el original.

Utilice cualquier código o resumen

El cifrado que se utiliza de forma predeterminada es el hash AES256 y SHA256 de la biblioteca estándar OpenSSL , ambos muy utilizados en criptografía. Sin embargo, puede utilizar cualquier cifrado y resumen (es decir, hash) disponible que sea compatible con OpenSSL (incluso los personalizados que proporcione).

Para ver qué algoritmos están disponibles, haga esto en la línea de comando:

 #get list of cipher providers openssl list -cipher-algorithms #get list of digest providers openssl list -digest-algorithms


Estos dos proporcionarán una lista de algoritmos de cifrado y resumen (hash). Algunos de ellos pueden ser más débiles que los predeterminados elegidos por Gliimly, y otros pueden estar allí solo por compatibilidad con sistemas más antiguos. Sin embargo, otros pueden ser bastante nuevos y no haber tenido suficiente tiempo para ser validados en la medida que usted desea.


Por lo tanto, tenga cuidado al elegir estos algoritmos y asegúrese de saber por qué está cambiando los predeterminados. Dicho esto, aquí hay un ejemplo de uso del cifrado Camellia-256 (es decir, "CAMELLIA-256-CFB1") con el resumen "SHA3-512". Reemplace el código en "encrypt.gliim" con:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt data encrypt-data str to enc_str password "my_password" \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out enc_str @ // Decrypt data decrypt-data enc_str password "my_password" to dec_str \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out dec_str @ end-handler


Realizar la solicitud:

 gg -q


Ejecútalo:

 gg -r --req="/encrypt" --silent-header --exec


En este caso, el resultado es:

 f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6 This contains a secret code, which is Open Sesame!

Nuevamente, obtienes los datos originales. Ten en cuenta que debes usar el mismo cifrado y resumen tanto en los datos cifrados como en los datos descifrados.

Por supuesto, puede producir el valor binario cifrado como antes utilizando las cláusulas "binary" y "output-length".

Si tienes sistemas externos que cifran datos y sabes qué cifrado y resumen utilizan, puedes hacer que tu código sea interoperable. Gliimly utiliza la biblioteca OpenSSL estándar, por lo que es probable que otros programas también lo hagan.

Usando sal

Para agregar sal al cifrado, utilice la cláusula "salt". Puede generar sal aleatoria utilizando la declaración random-string (o random-crypto si es necesario). Aquí está el código para "encrypt.gliim":

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Get salt random-string to rs length 16 // Encrypt data encrypt-data str to enc_str password "my_password" salt rs @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs to dec_str p-out dec_str @ end-handler


Realizar la solicitud:

 gg -q


Ejecútalo unas cuantas veces:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


El resultado:

 Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd This contains a secret code, which is Open Sesame! Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371 This contains a secret code, which is Open Sesame! Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917 This contains a secret code, which is Open Sesame!

Como puede ver, se genera un valor de sal aleatorio (de 16 bytes de longitud en este caso) para cada cifrado, y el valor cifrado es diferente cada vez, ¡aunque los datos que se cifran sean los mismos! Esto hace que sea difícil descifrar un cifrado como este.

Por supuesto, para descifrar, debes registrar la sal y usarla exactamente como lo hiciste al cifrar. En el código que se muestra aquí, la variable "rs" contiene la sal. Si almacenas los valores cifrados en la base de datos, probablemente almacenarías la sal justo al lado.

Vector de inicialización

En la práctica, no se debe utilizar un valor de sal distinto para cada mensaje. Se crea una clave nueva cada vez y eso puede reducir el rendimiento. Y en realidad no hay necesidad de ello: el uso de la sal es para que cada clave (incluso las mismas) sea mucho más difícil de adivinar. Una vez que haya hecho eso, es posible que no necesite hacerlo de nuevo o con tanta frecuencia.

En lugar de eso, se usaría un IV (vector de inicialización) para cada mensaje. Generalmente es una cadena aleatoria que hace que los mismos mensajes parezcan diferentes y aumenta el costo computacional de descifrar la contraseña. Aquí está el nuevo código para "encrypt.gliim":

 begin-handler /encrypt public // Get salt random-string to rs length 16 // Encrypt data start-loop repeat 10 use i start-with 0 random-string to iv length 16 encrypt-data "The same message" to enc_str password "my_password" salt rs iterations 2000 init-vector iv cache @The encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to dec_str cache p-out dec_str @ end-loop end-handler


Realizar la solicitud:

 gg -q


Ejecútalo unas cuantas veces:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


El resultado puede ser:

 The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2 The same message The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be The same message The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7 The same message The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056 The same message The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd The same message The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58 The same message The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40 The same message The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5 The same message The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458 The same message The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00 The same message

Puedes ver que el mismo mensaje aparece de forma diferente cuando está cifrado, pero cuando se descifra, vuelve a ser el mismo. Por supuesto, la contraseña, la sal, la cantidad de iteraciones y el vector de inicio deben ser los mismos tanto para el cifrado como para el descifrado.

Tenga en cuenta el uso de la cláusula "cache" en encrypt-data y decrypt-data. Efectivamente, almacena en caché la clave calculada (dada la contraseña, la sal, los algoritmos de cifrado/resumen y la cantidad de iteraciones), por lo que no se calcula cada vez que se pasa por el bucle. Con "cache", la clave se calcula una vez y luego se utiliza un IV diferente (en la cláusula "init-vector") para cada mensaje.

Si desea reconstruir la clave ocasionalmente, utilice la cláusula "clear-cache", que proporciona un valor booleano. Si es verdadero, se vuelve a calcular la clave; de lo contrario, se deja intacta. Consulte encrypt-data para obtener más información al respecto.

Conclusión

Aprendiste a cifrar y descifrar datos usando diferentes cifrados, resúmenes, valores de sal y de IV en Gliimly. También puedes crear un valor cifrado legible para humanos y una salida binaria, así como cifrar tanto cadenas como valores binarios (como documentos).