/////////////////////////////////////////////////////////// // CRIPTOGRAFIA - Técnicas de desarrollo para profesionales /////////////////////////////////////////////////////////// // Capítulo 4: Criptografía en entornos .NET // Sección 4.6: Codificaciones de casos prácticos // Apartado: 4.6.1: Cifrado simétrico de información /////////////////////////////////////////////////////////// // Listado 4.9: Ejemplo práctico para el cifrado de // información de manera simétrica. Algoritmo Rijndael. /////////////////////////////////////////////////////////// using System; using System.IO; using System.Text; using System.Security.Cryptography; public class CifradoSimetrico { // Este método encriptará el texto-plano utilizando Rijndael y // retornará el resultado encodeado en base-64. El parámetro de // frase-clave será usado para generar la llave. El salt se usará // de manera convencial. El algoritmo de hashing a utilizar // también será parámetro, como la cantidad de interaciones (para // la contraseña), el vector de inicialización y la longuitud de // la llave. public static string Encriptar(string textoPlano, string fraseClave, string salt, string algoritmoHash, int iteracionesPBE, string initVector, int longitudLlave) { // Convertir las cadenas de caracteres (ASCII) a arrays de bytes byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); byte[] saltBytes = Encoding.ASCII.GetBytes(salt); // Convertir el texto-plano (UTF-8) a un array de bytes byte[] textoPlanoBytes = Encoding.UTF8.GetBytes(textoPlano); // Generación de la llave (password); estándar PKCS#5 v2.0 PasswordDeriveBytes password = new PasswordDeriveBytes( fraseClave, saltBytes, algoritmoHash, iteracionesPBE); // Tomar los bytes de la llave byte[] llaveBytes = password.GetBytes(longitudLlave / 8); // Crear la instancia del objeto Rijndael para la encriptación RijndaelManaged cifrado = new RijndaelManaged(); // Establecer el modo de cifrado a CBC cifrado.Mode = CipherMode.CBC; // Generar la instancia de ICryptoTransform según la llave y el // vector de inicialización ICryptoTransform encryptor = cifrado.CreateEncryptor( llaveBytes, initVectorBytes); // Memoria en donde almacenaremos la información encriptada MemoryStream memoryStream = new MemoryStream(); // Instancia el CryptoStream a utilizar CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); // Comenzar la encriptación cryptoStream.Write(textoPlanoBytes, 0, textoPlanoBytes.Length); // Finalizar la encriptación cryptoStream.FlushFinalBlock(); // Convertir la información cifrada a un array de bytes byte[] textoCifradoBytes = memoryStream.ToArray(); // Cerrar los "streams" memoryStream.Close(); cryptoStream.Close(); // Convertir la información cifrada a una cadena encodeada en base-64 string textoCifrado = Convert.ToBase64String(textoCifradoBytes); // Retorno de la cadena cifrada o encriptada return textoCifrado; } // Este método desencriptará el texto-cifrado usando la implementación // del algoritmo Rijndael; de manera similar a cómo lo encripta los // datos el métood anterior. Nótese que los parámetros del algoritmo // de hashing, longitud de llave, vector de incialización, salt, número // de iteraciones y la frase-clave, por supuesto, deberán se los mismos // que aquellos utilizados en la encriptación. public static string Desencriptar(string textoCifrado, string fraseClave, string salt, string algoritmoHash, int iteracionesPBE, string initVector, int longitudLlave) { // Convertir cadenas de caracteres (ASCII) a arrays de bytes byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector); byte[] saltBytes = Encoding.ASCII.GetBytes(salt); // Convertir el texto-cifrado a un array de bytes byte[] textoCifradoBytes = Convert.FromBase64String(textoCifrado); // De manera similar a la del proceso de encriptación, generamos // la llave (password) PasswordDeriveBytes password = new PasswordDeriveBytes( fraseClave, saltBytes, algoritmoHash, iteracionesPBE); // Recuperamos los bytes de la llave byte[] llaveBytes = password.GetBytes(longitudLlave / 8); // Instancia del objeto Rijndael RijndaelManaged cifrado = new RijndaelManaged(); // Como para la encriptación, se establece el modo CBC cifrado.Mode = CipherMode.CBC; // Generar la instancia de ICryptoTransform según la llave y el // vector de inicialización; aquí, para la desencriptación ICryptoTransform decryptor = cifrado.CreateDecryptor( llaveBytes, initVectorBytes); // Memoria que almacena la información encriptada MemoryStream memoryStream = new MemoryStream(textoCifradoBytes); // Instancia de nuestro CryptoStream CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read); // Buffer para la información "desencriptada" byte[] textoPlanoBytes = new byte[textoCifradoBytes.Length]; // Comienzo de desencriptación int desencriptaByteCount = cryptoStream.Read(textoPlanoBytes, 0, textoPlanoBytes.Length); // Cerrar "streams" memoryStream.Close(); cryptoStream.Close(); // Convertir la información desencriptada a una cadena de caracteres (UTF-8) string textoPlano = Encoding.UTF8.GetString(textoPlanoBytes, 0, desencriptaByteCount); // Retorno del texto-plano; cadena de caracteres desencriptada return textoPlano; } } public class PruebaCifradoSimetrico { static void Main(string[] args) { // Nuestro texto-plano original string textoPlano = "Contenido de prueba"; // Nuestra frase-clave string fraseClave = "Frase secreta"; // Un valor de salt de ejemplo string salt = "12345678"; // Algoritmo de hash a utilizar para generar la // llave a partir de la frase-clave ("SHA1" o "MD5") string algoritmoHash = "SHA1"; // Cantidad de iteraciones para el algoritmo PBE int iteracionesPBE = 1000; // Vector de incialización de ejemplo (16 bytes) string initVector = "1234567890123456"; // Longitud de la llave (128, 192 o 256) int longitudLlave = 128; // Imprimir a consola el texto-plano original Console.WriteLine(String.Format("Texto-plano original: {0}", textoPlano)); // Realizamos la encriptación string textoCifrado = CifradoSimetrico.Encriptar(textoPlano, fraseClave, salt, algoritmoHash, iteracionesPBE, initVector, longitudLlave); // Imprimir a consola el texto-cifrado Console.WriteLine(String.Format("Texto-cifrado: {0}", textoCifrado)); // Realizamos la desencriptación textoPlano = CifradoSimetrico.Desencriptar(textoCifrado, fraseClave, salt, algoritmoHash, iteracionesPBE, initVector, longitudLlave); // Imprimir a consola el texto-plano obtenido Console.WriteLine(String.Format("Texto-plano obtenido: {0}", textoPlano)); } }