Aclaración
El siguiente contenido apareció publicado en la revista MTJ.NET en MSDN en español en el año 2005, en partes I y II. Debido a que la revista MTJ.NET no está al aire hoy, he optado por lanzarlo al aire nuevamente desde mi blog en un sólo documento.
A los suscriptores RSS les pido disculpas de antemano por haber publicado diferentes versiones.
Código fuente disponible aquí.
Introducción
Cuando es necesario darle seguridad a ciertos datos de nuestra aplicación, y buscamos información en el Web que nos permita lograrlo, es normal que hablemos (o leamos) de encriptación, y que irremediablemente salgan a la luz palabras tales como algoritmos simétricos, asimétricos y Hash. Estos términos pueden generar temor o dar la sensación de ser complejos. La motivación de este documento es mostrar qué es la encriptación sin necesidad de profundizar en cómo funciona un algoritmo internamente; y también indicar las malas prácticas que se realizan popularmente. Se revisará además la forma correcta de utilizar los algoritmos de encriptación, dónde se deben usar y dónde no.
Debido a lo extenso del tema en cuestión y a que es preferible realizar todas las explicaciones correspondientes, fue necesario dividir el documento en dos partes. Lo que contendrá cada parte está detallado en el siguiente índice.
Agradecimientos
Antes de comenzar deseo agradecer tanto el apoyo de Mike Giagnocavo como el de la información disponible en su blog (http://www.atrevido.net/). Sin sus consejos y correcciones, los ejemplos de este documento y la demostración no habrían quedado de excelente calidad y listos para utilizar.
Introducción, alcance y preguntas iniciales
La seguridad de los datos de cualquier sistema es algo muy importante, y no siempre recibe la atención y dedicación necesarias. Podemos formular las siguientes preguntas básicas y es probable que encontremos falencias de seguridad que pueden comprometer a nuestro sistema:
- ¿Se está almacenando la información crítica de forma segura?
- ¿Qué entendemos por seguro?
- Si se está encriptando, ¿Qué algoritmo se está utilizando?
- ¿Se permite la desencriptación de datos que no debieran poder desencriptarse?
- ¿Cómo está realizándose la encriptación?
Adicionalmente, podemos complementar con preguntas más complejas:
- ¿Dónde están almacenadas la o las llaves?
- ¿Con qué frecuencia se cambian las llaves?
- ¿Cómo se enfrenta el problema de cambiar las llaves con tanta frecuencia?
- ¿Con qué criterio se definen las nuevas llaves?
Es probable que algunas de las preguntas básicas no sean respondidas adecuadamente. Si esto es así, sería recomendable hacer una revisión de los criterios y metodologías que se están utilizando, y determinar si es necesario hacer modificaciones o rehacer esas funcionalidades. Si el pensamiento es que esto no genera utilidad, cabe hacerse las siguientes preguntas: ¿Qué le sucedería al negocio si alguien accediese a cualquiera de los datos privados del sistema? ¿Se verá comprometida la seguridad con hechos como éste? De seguro, en ese momento el pensamiento sería que valdría la pena. Entonces, manos a la obra.
El alcance de este documento es dar a conocer los distintos métodos que existen para proteger tanto el almacenamiento como el envío de la información. Este documento NO tiene como intención ser un manual de seguridad. Esto es lo mínimo que debe tener una organización para no estar al descubierto en sus procesos. Se debe entender que existen libros dedicados a la seguridad. Aquí se está danto el puntapié inicial para salir del desconocimiento que existe sobre este tema. Es responsabilidad tanto de los administradores de sistemas operativos como del arquitecto de soluciones definir las reglas, dónde almacenar sus llaves, los criterios de cambio de llaves y también los lugares donde almacenará la información.
Habiendo introducido el tema y definido el alcance, comencemos con la revisión de los algoritmos y los distintos tipos que existen.
Definición de algoritmos
Como se mencionó con anterioridad, existen distintos tipos de algoritmos de encriptación, pero algo que no se ha mencionado hasta ahora son los “algoritmos” que se cree que encriptan, pero que no hacen nada más que transformar texto (o información). El objetivo de hacer esta distinción es mostrar algunas malas prácticas que se cometen, pensando que cualquier algoritmo que cambia de cierta forma un texto, está realizando encriptación de datos. Estos algoritmos podremos llamarlos simplemente "Transformaciones".
Como se está hablando de encriptación, se revisarán también los algoritmos que han sido construidos con distintos fines y que están agrupados en distintas categorías. ¿Cuál es la funcionalidad de los algoritmos dentro de cada uno de estos grupos? Cada uno tiene un objetivo y un problema que atacar, y por lo mismo hay casos en los que debe utilizarse uno y sólo uno de estos algoritmos, como también casos en los que debe utilizarse algoritmos de más de un grupo.
Antes de revisar los escenarios, se debe aclarar el concepto de llave. La llave de encriptación es una serie de carácteres, de determinado largo, que se utiliza para encriptar y desencriptar la información que se quiere proteger. El largo de la llave depende del algoritmo. Existen llaves privadas y públicas, que serán detalladas más adelante. Veamos los siguientes escenarios.
- Escenario 1: Necesito almacenar información crítica que deberá poder descifrarse, y seré yo el único que haga todo el proceso. Nadie más tendrá acceso a la llave con que se encriptará y desencriptará la información.
- Escenario 2: Necesito que me envíen información crítica que yo desencriptaré posteriormente, pero necesito que las personas que me van a enviar información pueden encriptarla libremente, pero no desencriptarla. En este caso, se deja disponible una llave pública para que ellos encripten y yo tendré mi llave privada de encriptación en forma segura.
- Escenario 3: Necesito almacenar o enviar información crítica de forma segura, pero que no requerirá ser desencriptada para su validación, o que es extremadamente importante verificar que no haya sido modificada en el camino.
Estos son los tres escenarios más comunes. Es probable que te preguntes para qué puede haber casos como el 3. Lo veremos más adelante con ejemplos, destacando ventajas y desventajas. Los tres escenarios calzan con los algoritmos listados a continuación:
- Escenario 1: Encriptación simétrica
- Escenario 2: Encriptación asimétrica
- Escenario 3: Hash de información
Antes de comenzar a revisar cada uno de estos algoritmos, revisemos una muy mala práctica que se utiliza más de lo que debiera, que corresponde a la utilización de “algoritmos” o mejor dicho, transformaciones del contenido.
Malas prácticas o transformaciones
Antes mencionamos que es común escuchar o ver que se utilizan ciertas transformaciones como métodos de protección. Como la comparación se hace con el ojo humano, si se tiene una cadena de texto y se decide transformar aplicándole un XOR o desplazando los bytes, el resultado de esa transformación nos produce el efecto de que está encriptado, por que no lo podemos entender (nuestro cerebro no lo entiende). Debido a esto, no vamos a hacer el ejemplo transformando texto sino que lo haremos transformando una imagen. Se aplicarán los dos efectos mencionados recién, XOR y Desplazamiento, y se verá que es efectivamente lo que hacen. El motivo de utilizar una imagen es para que nuestro ojo no se engañe con las apariencias.
Nuestro ejemplo nos presenta una pantalla como la que se muestra en la Figura 1, donde tenemos dos botones; uno para cargar la imagen, otro para aplicarle las transformaciones XOR, y otro para desplazar los bytes:

Figura 1.
Si se presiona Transformar, el resultado será el que se muestra en la figura 2:

Figura 2: Resultado de las transformaciones.
Como se puede apreciar, no se produce una encriptación sino sólo una transformación de los datos (más adelante verás cómo queda la imagen realmente encriptada). Este tipo de transformaciones no protege nuestra información. Hagamos ahora las siguientes preguntas: ¿Cuánta seguridad existe con estas transformaciones? ¿Cuánto demorará un hacker en retransformar esto? El hacker obtiene la respuesta en menos de lo que te toma a ti leer sólo esta línea. Hacerle modificaciones a este “algoritmo” no sirve. La encriptación no es trivial de hacer, pero es mucho más fácil de usar. Más adelante conocerás la cantidad de años que estuvo estudiándose el algoritmo Rijndael o AES, para ser declarado vencedor.
El código que utilizamos para realizar la transformación XOR es el siguiente. El código de desplazamiento es muy similar. Este, al igual que todo el código del documento, está disponible en el archivo adjunto con este artículo. Por motivos que no corresponde explicar aquí, ya que no entran en el objetivo del documento, con la imagen que se está trabajando es necesario comenzar desde la posición 34, ya que si se comienza desde el byte 0 transformando, el archivo BMP ya no es posible visualizarlo.
Lo único que hace este “algoritmo” es aplicar XOR a un arreglo de bytes. Para aplicarlo sobre texto lo único que hay que hacer son las transformaciones de bytes a texto y viceversa. Como explicación sencilla de este proceso, podemos decir que lo que hace es tomar cada uno de los bytes y aplicarle un XOR (representado por el símbolo "^" en el código). Un XOR corresponde al resultado de la comparación bit a bit de dos bytes. En este caso, un byte viene del texto a transformar y el otro del número 87 elegido al azar.
|
public class TransformacionXOR { private static int _XOR = 87;
public static byte[] Codificar(byte[] bytACodificar) { for (int _intPos = 34; _intPos < bytACodificar.Length; _intPos++) bytACodificar[_intPos] = (byte) (_XOR ^ bytACodificar[_intPos]); return bytACodificar; }
public static byte[] DeCodificar(byte[] bytADecodificar) { for (int _intPos = 34; _intPos < bytADecodificar.Length; _intPos++) bytADecodificar[_intPos] = (byte) (_XOR ^ bytADecodificar[_intPos]); return bytADecodificar; } }
|
Como ven, este algoritmo tan lineal no nos da ninguna seguridad; y por lo mismo, jamás debe utilizarse para proteger información. No debe creerse que es XOR el responsable de esto. XOR es muy útil en otros contextos, pero no se puede utilizar de esta forma.
Nota: Antes de continuar, es muy importante aclarar lo siguiente. Si se va a proteger información, el proceso DEBE realizarse a conciencia y con algoritmos probados. Además, se debe utilizar el algoritmo de la forma en que fue concebido. Implementaciones a medias, son tan inútiles como las transformaciones recién vistas o como la no utilización de ésta. O el proceso se hace bien, o no se hace.
Veamos ahora los algoritmos reales de encriptación.
Encriptación simétrica
Cuando hablamos de encriptación y no de transformación, ya estamos adentrándonos en temas de mayor protección, de algoritmos conocidos y seguridad real. El proceso de realizar una encriptación es complejo para ser entendido por nosotros mismos, pero no es limitante para conocer cuáles son los pasos para utilizarlos y qué errores no se deben cometer.
Dentro de los algoritmos de encriptación simétrica podemos encontrar los siguientes, algunos más seguros que otros.
DES (Digital Encryption Standard)
Creado en 1975 con ayuda de la NSA (National Security Agency); en 1982 se convirtió en un estándar. Utiliza una llave de 56 bit. En 1999 logró ser quebrado (violado) en menos de 24 horas por un servidor dedicado a eso. Esto lo calificó como un algoritmo inseguro y con falencias reconocidas.
3DES (Three DES o Triple DES)
Antes de ser quebrado el DES, ya se trabajaba en un nuevo algoritmo basado en el anterior. Este funciona aplicando tres veces el proceso con tres llaves diferentes de 56 bits. La importancia de esto es que si alguien puede descifrar una llave, es casi imposible poder descifrar las tres y utilizarlas en el orden adecuado. Hoy en día es uno de los algoritmos simétricos más seguros.
IDEA (International Data Encryption Algorithm)
Más conocido como un componente de PGP (encriptación de mails), trabaja con llaves de 128 bits. Realiza procesos de shift y copiado y pegado de los 128 bits, dejando un total de 52 sub llaves de 16 bits cada una. Es un algoritmo más rápido que el DES, pero al ser nuevo, aún no es aceptado como un estándar, aunque no se le han encontrado debilidades aún.
AES (Advanced Encryption Standard)
Este fue el ganador del primer concurso de algoritmos de encriptación realizado por la NIST (National Institute of Standards and Technology) en 1997. Después de 3 años de estudio y habiendo descartado a 14 candidatos, este algoritmo, también conocido como Rijndael por Vincent Rijmen y Joan Daemen, fue elegido como ganador. Aún no es un estándar, pero es de amplia aceptación a nivel mundial.
En nuestra demo utilizaremos el algoritmo AES. .NET provee implementaciones de AES, DES y TripleDES entre otros.
El algoritmo más seguro hoy el AES, aunque 3DES también es muy seguro. Este último se utiliza cuando hay necesidad de compatibilidad. AES 128 es aproximadamente 15% más rápido que DES, y AES 256 sigue siendo más rápido que DES.
Cualquiera de estos algoritmos utiliza los siguientes dos elementos; ninguno de los dos debe pasarse por alto ni subestimar su importancia:
IV (Vector de inicialización)
Esta cadena se utiliza para empezar cada proceso de encriptación. Un error común es utilizar la misma cadena de inicialización en todas las encriptaciones. En ese caso, el resultado de las encriptaciones es similar, pudiendo ahorrarle mucho trabajo a un hacker en el desciframiento de los datos. Tiene 16 bytes de largo. Puedes encontrar más información acerca de IV en http://www.atrevido.net/blog/PermaLink.aspx?guid=6136ce81-9fa1-4995-bb3e-15bc5f1f5899.
Key (llave)
Esta es la principal información para encriptar y desencriptar en los algoritmos simétricos. Toda la seguridad del sistema depende de dónde esté esta llave, cómo esté compuesta y quién tiene acceso. El largo de las llaves depende del algoritmo. Al final del documento se darán algunas recomendaciones para el almacenamiento, generación y rotación de llaves.
|
Algoritmo
|
Largos válidos (bits)
|
Largo por defecto (bits)
|
| DES |
64 |
64 (8 bytes) |
| TripleDES |
128, 192 |
192 (24 bytes) |
| Rijndael |
128, 192, 256 |
256 (32 bytes) |
|
Veamos ahora nuestra aplicación y cómo funciona para encriptación y desencriptación con algoritmos simétricos, específicamente Rijndael (Ver figura 3):

Figura 3: Resultado de la encriptación con Rijndael aplicado sobre una imagen y texto.
Lo primero que debemos hacer es especificar una llave de encriptación. Una cadena de texto se utiliza en el ejemplo. En la caja de más abajo se puede ingresar el texto que se desea encriptar. Luego de presionar "Encriptar" se obtiene el resultado de la encriptación en la caja del medio, y como es de esperarse, presionando "DesEncriptar" se obtiene el texto desencriptado en la última caja de texto.
Si revisamos el código fuente que realiza la encriptación y desencriptación, encontraremos algo de mayor complejidad con respecto las transformaciones anteriormente vistas.
|
public class MiRijndael { public static byte[] Encriptar(string strEncriptar, byte[] bytPK) { Rijndael miRijndael = Rijndael.Create(); byte[] encrypted = null; byte[] returnValue = null;
try { miRijndael.Key = bytPK; miRijndael.GenerateIV();
byte[] toEncrypt = System.Text.Encoding.UTF8.GetBytes(strEncriptar); encrypted = (miRijndael.CreateEncryptor()).TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
returnValue = new byte[miRijndael.IV.Length + encrypted.Length]; miRijndael.IV.CopyTo(returnValue, 0); encrypted.CopyTo(returnValue, miRijndael.IV.Length); } catch { } finally { miRijndael.Clear(); }
return returnValue; }
public static string Desencriptar(byte[] bytDesEncriptar, byte[] bytPK) { Rijndael miRijndael = Rijndael.Create(); byte[] tempArray = new byte[miRijndael.IV.Length]; byte[] encrypted = new byte[bytDesEncriptar.Length - miRijndael.IV.Length]; string returnValue = string.Empty;
try { miRijndael.Key = bytPK;
Array.Copy(bytDesEncriptar, tempArray, tempArray.Length); Array.Copy(bytDesEncriptar, tempArray.Length, encrypted, 0, encrypted.Length); miRijndael.IV = tempArray;
returnValue = System.Text.Encoding.UTF8.GetString((miRijndael.CreateDecryptor()).TransformFinalBlock(encrypted, 0, encrypted.Length));
} catch { } |