Programación III, Guía 11 1 Facultad : Ingeniería Escuela : Computación Asignatura: Programación III Tema: “CIFRADO”. Objetivo Distinguir entre técnicas de cifrado Simétrico y Asimétrico Realizar ejercicios de cifrado por medio de los cuales se comprenderá el funcionamiento e importancia del mismo. Introducción Cuando dos partes remotas intercambian datos a través de un canal inseguro (como por ejemplo), ambas partes deben garantizar que los datos comunicados: • • • No puedan ser comprendidos por nadie que pueda estar escuchando: Confidencialidad. No han sido modificados durante la trasmisión: Integridad. Provienen realmente de quién provienen y no de nadie que haya suplantado la identidad de una de las dos partes: Autenticación. La criptografía se utiliza para lograr estos objetivos. Para alcanzarlos es necesario la combinación de una serie de primitivas criptográficas. Aunque este tema da la sensación de ser complejo, en realidad, no es necesario saber cómo funcionan los algoritmos de encriptación. El truco es saber usarlos sin más y evitar malas prácticas. Consejo: Cualquier algoritmo que cambie de cierta manera un texto no se puede considerar un algoritmo encriptado, sino simplemente “transformador” (por ejemplo, aplicar un XOR a una cadena de texto produce un resultado que, como no lo entendemos, creemos que la información está encriptado, cuando en realidad un hacker podría obtener la información fácilmente). Así que, si vamos a proteger información, debe hacerse con algoritmos probados. Programación III, Guía 11 2 Cifrado Simétrico con C#. 1ª Primitiva criptográfica: La encriptación simétrica o cifrado de clave secreta. En la encriptación simétrica se utiliza una única clave secreta para cifrar y descifrar los datos (de ahí la denominación de simétrica). Son algoritmos muy rápidos, por eso se utilizan para grandes secuencias de datos. Dentro de los algoritmos de encriptación simétrica podemos encontrar los siguientes (algunos más seguros que otros). • DES (Digital Encryption Standard) • 3DES (Three DES o Triple DES) • IDEA (International Data Encryption Algorithm) • AES (Advanced Encryption Standard) AES, también conocido como Rijndael (aunque no son exactamente lo mismo), es de amplia aceptación a nivel mundial. Hoy en día es el más seguro y rápido. Cualquiera de estos algoritmos utiliza los siguientes dos elementos (ninguno de los dos debe pasarse por alto ni subestimarse su importancia): • IV (Vector de inicialización). No se puede encriptar sin él. Es de 16 bytes de longitud para el algoritmo de Rijndael. No es una 2ª llave, por lo tanto, no se trata de una dato que haya que esconder, únicamente hay que considerar que hay que usar el mismo IV para encriptar/desencriptar un mensaje concreto. Un error común es utilizar el mismo vector de inicialización en todas las encriptaciones. Utilizar siempre un mismo IV es equivalente en seguridad a no utilizar encriptación. • Key (llave). Esta es la principal información para encriptar / desencriptar en los algoritmos simétricos. Toda la seguridad de un sistema simétrico depende de dónde esté esta llave, cómo esté compuesta y quién tiene acceso. Éste es un dato que debe conocerse única y exclusivamente por los interlocutores de la comunicación. De otra forma, la seguridad en la comunicación se vería comprometida. Ejemplo de funcionamiento. • • • Ana y José acuerdan utilizar un algoritmo en particular con una clave y vector de inicialización concretos. Ana redacta un mensaje y cifra el texto usando la clave y el vector de inicialización y se lo envía a José por Internet. José recibe el texto cifrado y lo descifra utilizando la clave y el vector acordados anteriormente. Si se intercepta la transmisión, el interceptor no podrá recuperar el mensaje original porque no conoce la clave ni el vector. Programación III, Guía 11 3 Consideraciones acerca de la clave. Cuanto más grande sea el tamaño de la llave, más difícil será obtenerla mediante un ataque por fuerza bruta. Para el algoritmo de Rijndael, las claves pueden ser de 128, 192 y 256 bits de longitud. Por ejemplo, para una clave de 64 bits de longitud, a un ritmo de comprobación de 50 claves por segundo, podría llevar unos 11,6 billones de años en comprobar todos los valores posibles de la clave. Consideraciones acerca del vector de inicialización. En un esquema de cifrado por bloques (como Rijndael), la secuencia de texto sin cifrar se parte en bloques para su procesamiento. Para una clave secreta determinada, un cifrado que no utilice un vector de inicialización codificará el mismo bloque de entrada de texto sin cifrar en el mismo bloque de salida de texto cifrado. Si hay bloques duplicados dentro la secuencia de texto a cifrar, habrá bloques duplicados en la secuencia de texto cifrado. Si el hacker sabe algo acerca de la estructura de un bloque del texto sin cifrar, puede utilizar esa información para descifrar el bloque de texto cifrado conocido y, posiblemente, recuperar la clave. Para combatir este problema, la información del bloque anterior se mezcla en el proceso de cifrado del bloque siguiente. Así pues, el resultado de dos bloques idénticos de texto sin cifrar es distinto. Como esta técnica utiliza el bloque anterior para cifrar el bloque siguiente, se utiliza un IV para cifrar el primer bloque de datos. Con este sistema, los encabezados de los mensajes comunes que un hacker podría conocer no pueden utilizarse para aplicar técnicas de ingeniería inversa en una clave. Consideraciones acerca del modo de cifrado Los algoritmos de cifrado de bloque como DES o AES separan el mensaje en bloques de tamaño fijo para su procesamiento, por ejemplo 128 bits. La forma en que se gestionan estos bloques se denomina “modo de cifrado”. Los modos de cifrado que soporta .NET Framework son ECB, CBC, y CFB (es preferible usar CBC). Modos de relleno. La mayoría de los mensajes de texto a cifrar no contienen los bytes necesarios para rellenar totalmente los bloques. A menudo, no hay bytes suficientes para rellenar el último bloque. Cuando esto sucede, se agrega una cadena de relleno al texto. Por ejemplo, si la longitud del bloque es de 64 bits y el último bloque sólo contiene 40 bits, se agregan 24 bits de relleno. .NET Framework soporta dos modos de relleno: • • Zeros: La cadena de relleno consta de bytes establecidos en cero. PKCS7: La cadena de relleno PKCS #7 consta de una secuencia de bytes, en la que cada byte es igual al número total de bytes de relleno agregados. Programación III, Guía 11 4 Cifrado Asimétrico con C#. Es el método criptográfico que usa un par de claves para el envío de mensajes. Las dos claves pertenecen a la misma persona que ha enviado el mensaje. Una clave es pública y se puede entregar a cualquier persona, la otra clave es privada y el propietario debe guardarla de modo que nadie tenga acceso a ella. Además, los métodos criptográficos garantizan que esa pareja de claves sólo se puede generar una vez, de modo que se puede asumir que no es posible que dos personas hayan obtenido casualmente la misma pareja de claves. Si el remitente usa la clave pública del destinatario para cifrar el mensaje, una vez cifrado, sólo la clave privada del destinatario podrá descifrar este mensaje, ya que es el único que la conoce. Por tanto se logra la confidencialidad del envío del mensaje, nadie salvo el destinatario puede descifrarlo. En esta guía se describe cómo utilizar las clases de cifrado que proporciona Microsoft .NET Framework para cifrar un archivo de texto en un estado ilegible y descifrar después ese archivo para devolverlo a su estado original. El espacio de nombre System.Security.Cryptographic de Microsoft .NET Framework proporciona diversas herramientas para ayudarle con el cifrado y el descifrado. La clase CryptoStream es una de las muchas clases que se proporcionan. La clase CryptoStream está para cifrar o descifrar el contenido a medida que se transmite en secuencias a un archivo. Programación III, Guía 11 5 Materiales y equipo • • Guía de Laboratorio Nº 11. Computadora con programa: o Visual Studio C# • Dispositivo de Almacenamiento (USB/opcional). Procedimiento Ejercicio 1: Cree un proyecto modo consola en C# .NET. Con el nombre de encryption. Programación III, Guía 11 6 Programación III, Guía 11 7 Programación III, Guía 11 8 Ejercicio 2: En el escritorio cree una carpeta de manera que coincida con la ruta dirPruebas. A continuación cree un proyecto modo consola en C# .NET. Con el nombre de cifradoRSA_CS. using using using using System; System.Text; System.Security.Cryptography; System.IO; namespace cifradoRSA_CS { class Program { private static string dirPruebas = @"C:\Users\usuario\Desktop\Guia de Cifrado\cifradoRSA_CS\RSA cripto"; private static string ficPruebas = Path.Combine(dirPruebas, "MisClaves_CS.xml"); static void Main() { // Cifrar y descifrar con RSA Console.Title = "Cifrar y descifrar con RSA"; // Si no existe el fichero de claves if (File.Exists(ficPruebas) == false) { crearXMLclaves(ficPruebas); } string xmlKeys = clavesXML(ficPruebas); byte[] datos = cifrar("Hola RSA C#", xmlKeys); string res = descifrar(datos, xmlKeys); Console.WriteLine(res); Console.ReadLine(); } private static void crearXMLclaves(string ficPruebas) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); string xmlKey = rsa.ToXmlString(true); // Si no existe el directorio, crearlo string dirPruebas = Path.GetDirectoryName(ficPruebas); if (Directory.Exists(dirPruebas) == false) { Directory.CreateDirectory(dirPruebas); } Programación III, Guía 11 9 using (StreamWriter sw = new StreamWriter(ficPruebas, false,Encoding.UTF8)) { sw.WriteLine(xmlKey); sw.Close(); } } private static string clavesXML(string fichero) { string s; using (StreamReader sr = new StreamReader(fichero,Encoding.UTF8)) { s = sr.ReadToEnd(); sr.Close(); } return s; } private static byte[] cifrar(string texto, string xmlKeys) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(xmlKeys); byte[] datosEnc = rsa.Encrypt(Encoding.Default.GetBytes(texto), false); return datosEnc; } private static string descifrar(byte[] datosEnc, string xmlKeys) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(xmlKeys); byte[] datos = rsa.Decrypt(datosEnc, false); string res = Encoding.Default.GetString(datos); return res; } } } Ejemplo 2: Antes de crear la aplicación debe tomar en cuenta la ruta donde será guardada (ver parte final del código) y deberá crear los tres archivos .txt a utilizar (Mydata.txt, Encryted.txt, Decrypt.txt) Posterior a ello cree un proyecto modo consola en C# .NET. Con el nombre de CSEncryptDecrypt using using using using using using System; System.IO; System.Security; System.Security.Cryptography; System.Runtime.InteropServices; System.Text; namespace CSEncryptDecrypt { class class1 Programación III, Guía 11 10 { // Llamar a esta función para quitar la clave de la memoria después de su uso por seguridad [System.Runtime.InteropServices.DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")] public static extern bool ZeroMemory(IntPtr Destination, int Length); // Función para generar una clave de 64 bits. static string GenerateKey() { // Crear una instancia del algoritmo simétrico. La clave y el IV se generan automáticamente. DESCryptoServiceProvider desCrypto = (DESCryptoServiceProvider)DESCryptoServiceProvider.Create(); // Utilizar la clave generada automáticamente para el cifrado. return ASCIIEncoding.ASCII.GetString(desCrypto.Key); } static void EncryptFile(string sInputFilename, string sOutputFilename, string sKey) { FileStream fsInput = new FileStream(sInputFilename, FileMode.Open, FileAccess.Read); FileStream fsEncrypted = new FileStream(sOutputFilename, FileMode.Create, FileAccess.Write); DESCryptoServiceProvider DES = new DESCryptoServiceProvider(); DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey); DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey); ICryptoTransform desencrypt = DES.CreateEncryptor(); CryptoStream cryptostream = new CryptoStream(fsEncrypted, desencrypt, CryptoStreamMode.Write); byte[] bytearrayinput = new byte[fsInput.Length]; fsInput.Read(bytearrayinput, 0, bytearrayinput.Length); cryptostream.Write(bytearrayinput, 0, bytearrayinput.Length); cryptostream.Close(); fsInput.Close(); fsEncrypted.Close(); } static void DecryptFile(string sInputFilename, string sOutputFilename, string sKey) { DESCryptoServiceProvider DES = new DESCryptoServiceProvider(); //Se necesita una clave de 64 bits y un IV para este proveedor. //Establecer la clave secreta para el algoritmo DES. DES.Key = ASCIIEncoding.ASCII.GetBytes(sKey); //Establecer el vector de inicialización. DES.IV = ASCIIEncoding.ASCII.GetBytes(sKey); //Crear una secuencia de archivo para volver a leer el archivo cifrado. FileStream fsread = new FileStream(sInputFilename, FileMode.Open, FileAccess.Read); Programación III, Guía 11 11 //Crear un descriptor de DES desde la instancia de DES. ICryptoTransform desdecrypt = DES.CreateDecryptor(); //Crear una secuencia de cifrado para leer y realizar una transformación de //cifrado DES en los bytes de entrada. CryptoStream cryptostreamDecr = new CryptoStream(fsread, desdecrypt, CryptoStreamMode.Read); //Imprimir el contenido del archivo descifrado. StreamWriter fsDecrypted = new StreamWriter(sOutputFilename); fsDecrypted.Write(new StreamReader(cryptostreamDecr).ReadToEnd()); fsDecrypted.Flush(); fsDecrypted.Close(); } static void Main() { // Debe ser 64 bits, 8 bytes. // Distribuir esta clave al usuario que descifrará este archivo. string sSecretKey; // Obtener la clave para que el archivo se cifre. sSecretKey = GenerateKey(); // Para mayor seguridad, fijar la clave. GCHandle gch = GCHandle.Alloc(sSecretKey, GCHandleType.Pinned); // Cifrar el archivo… EncryptFile(@"C:\CSEncryptDecrypt\MyData.txt", @"C:\CSEncryptDecrypt\Encrypted.txt", sSecretKey); // Descifrar el archivo. DecryptFile(@"C:\CSEncryptDecrypt\Encrypted.txt", @"C:\CSEncryptDecrypt\Decrypted.txt", sSecretKey); // Quitar la clave de la memoria. ZeroMemory(gch.AddrOfPinnedObject(), sSecretKey.Length * 2); gch.Free(); } } } Análisis de resultados • • • • Modifique el programa 1 de forma que el usuario introduzca su propio mensaje a codificar Analizar ¿considera eficiente este tipo de cifrado? ¿Qué diferencia encuentran entre el primer ejemplo con los ejemplos 2 y 3? ¿Qué modificaciones podría considerarse para mejorar el funcionamiento? Programación III, Guía 11 12 Investigación complementaria. • Investigar si el segundo programa utilizado en la práctica puede ser modificado de manera que tome un archivo .doc para el encriptamiento. Referencia. • • http://support.microsoft.com/kb/307010/es http://copstone.com/2010/03/encriptacion-en-c-sharp/ Programación III, Guía 11 13 Hoja de cotejo: 11 Guía 11: Cifrado. Alumno: Máquina No: Docente: GL: Fecha: EVALUACION % CONOCIMIENTO 40 APLICACIÓN DEL CONOCIMIENTO 40 ACTITUD 20 TOTAL 100% 1-4 5-7 8-10 Nota