La preocupación por la privacidad de la información y cómo proteger nuestros sistemas y comunicaciones de accesos no autorizados nos está obligando poco a poco a agregar medidas de protección adicionales frente a amenazas cada vez más generalizadas.
En un análisis de vulnerabilidades, aparte de los conocidos, temidos, y a veces sobrevalorados “hackers”, poco a poco hemos ido detectando un nuevo tipo de amenazas, y son los propios usuarios del sistema, que conforme van aumentando su formación en las nuevas tecnologías, utilizan las redes corporativas para acceder a información desprotegida.
En la mayoría de las ocasiones, una buena configuración de red o de usuarios y permisos debería mantener a salvo la información, pero con demasiada frecuencia las contraseñas de otros usuarios son de dominio casi público y no se cambian con la frecuencia necesaria. Más preocupante es que el enemigo sea el administrador de la red o tener el servicio de mantenimiento contratado a terceros con una rotación de personal alta.
Una de las contramedidas más eficaces para garantizar la privacidad de información almacenada en nuestro ordenador, es el propio cifrado de disco que nos ofrece Windows. Pero si nuestra clave es conocida por otro usuario o no bloqueamos el ordenador cada vez que nos levantamos de la mesa podemos estar dejando nuestra información de nuevo al descubierto, ya que aunque la información “está cifrada” entre los distintos usuarios, la mayoría de las veces el asalto no proviene de la red o sótanos oscuros, si no de los propios empleados, que aprovechan cualquier descuido para acceder al PC desprotegido.
Si trabajamos con un portátil, el riesgo aumenta considerablemente, cualquier persona con el suficiente interés en acceder a nuestra información sabe que es mucho más fácil robárnoslo que intentar complicadas técnicas de asalto a costa de horas de sueño
Distinguiendo tres tipos de ataque a un sistema de información consideramos ataques por:
Frente a estas situaciones, podemos dotar a nuestras aplicaciones de diferentes mecanismos de cifrado para cada una de las situaciones de uso previstas, diferenciando, de manera muy general, los tres tipos de cifrado conocidos en:
Ya que en las referencias bibliográficas no he encontrado aún ejemplos útiles de cifrado en .Net, y que, en la mayoría de las ocasiones la documentación disponible en la MSDN es demasiado escueta, el objetivo de este artículo es hacer una pequeña introducción a los objetos de cifrado más significativos en .Net, y cómo utilizarlos en cada caso de la manera adecuada con ejemplos concretos de implementación.
Las funciones HASH o resumen se basan en realizar un cálculo que devuelve un valor de longitud fija sobre el texto que deseamos cifrar.
Este cifrado no es reversible, y normalmente se utiliza para almacenar claves de usuario (al realizar la verificación de identidad, se calcula el valor HASH de la contraseña introducida, y si coincide con el HASH de la almacenada, se considera correcta). La gran ventaja de un cifrado HASH es que accediendo, por ejemplo a la tabla de usuario/contraseña almacenada en un directorio la información no es comprometedora (Fig 1)
Tabla de
usuario/clave sin cifrado HASH
|
Tabla de
usuario/clave con cifrado HASH
|
También podemos agregar un nivel de seguridad a nuestras bases de datos con este cifrado unidireccional. Incluyendo una tabla maestra de nombre-HASH también protegeremos la información, aunque no es nada que no nos resuelvan los identificadores largos de SQL Server (y resulta más eficiente)
En .Net disponemos, dentro del espacio de nombres System.security.Cryptography disponemos de varias versiones del SHA para cifrados unidireccionales.
Dependiendo de la seguridad y del rendimiento que necesitemos (cuanta mayor sea la longitud de cifrado es más seguro, pero tarda más en calcularse), consideramos
SHA1, SHA256, SHA384 y SHA512
Un ejemplo de una implementación de SHA sería
Public Function encrypt512(ByVal message As String ) As String Dim data() As Byte = System.Text.Encoding.ASCII.GetBytes(message) Dim result() As Byte Dim shaM As New System.Security.Cryptography.SHA512Managed result = shaM.ComputeHash(data) Dim encrypted As String = System.Text.Encoding.ASCII.GetString(result) Return encrypted End Function
Con las siguientes consideraciones:
En este tipo de cifrado, disponemos de una sola clave que sirve para cifrar y descifrar. Está especialmente indicado para cifrados en los que no hemos de compartir la información, por ejemplo, cifrar documentos confidenciales de mi portátil para que si lo pierdo, nadie excepto yo pueda devolver los archivos a texto “claro”.
Incorpora dos ventajas, la primera, que el cifrado del texto añade “autenticidad” a nuestro mensaje, ya que, cualquier transformación (ataque por modificación) en la cadena de bytes que lo compone nos impedirá revertir el cifrado.
Por otro lado, frente a las suplantaciones de identidad, podremos saber que cualquier mensaje que recibamos ha sido cifrado, con seguridad, por alguien con el que hemos acordado la clave de cifrado.
El inconveniente para este tipo de cifrado es, definitivamente, cómo hacer llegar al destinatario por canales inseguros una información tan crítica como la clave de cifrado (aparte de que no sabemos dónde la almacenará)
Como decíamos antes, la fortaleza de estos cifrados está limitada a la capacidad que tengamos para almacenar nuestra clave (que puede ir desde los 16 a los 32 caracteres) de manera segura.
Esta tarea no es trivial, ya que una clave de 16 caracteres aleatorios siempre acabará apuntada en un papel, en una agenda,…
Por otro lado, si guardamos cifrada la clave, ¿donde almacenamos la clave de cifrado para descifrar la clave original? Desde luego, en el código del programa o en el registro de Windows, no.
Existe una opción en .Net consistente en pedirle a Windows que nos la guarde cifrada:
Public sub guardarClave(clave as string ) ‘Cifra la clave y la guarda en ‘C:\Documents And Settings\<nombre usuario>\Application data\emskey Settings.saveEncrypted(“EMSKEY”,clave) End Sub
Para recuperarla
Public function recuperarClave(clave as string ) as string Return Settings.LoadEncrypted(“EMSKEY”) End Sub
Sin embargo, por encriptada que esté, el programa accede a ella, de manera que si alguien accede a nuestro ordenador y ejecuta el programa, tendrá acceso a la clave y a los datos.
Si protegemos el programa con una clave, será tan seguro como la clave que introduzca el usuario, volviendo al punto de partida.
La solución mejor, basándonos en los niveles de seguridad, es almacenar la clave en un dispositivo externo, con un doble beneficio:
Por un lado, el usuario ya no tiene que recordarla (incluso ni que saberla), sólo tiene que conectar el dispositivo.
Por otro lado, garantizamos que es el propietario de la clave el que accede a los datos, ya que nadie excepto él tendrá el dispositivo con la clave (salvo que no la olvide conectada al ordenador, claro)
Lógicamente, es deseable que este dispositivo no sea una llave de memoria USB convencional, ya que cualquiera podría extraer la información fácilmente.
En este aspecto, destacamos los siguientes dispositivos de almacenamiento:
En cualquier caso, recomendamos tener como referencia la web de Aladdin (www.aladdin.com ) para obtener información de estos elementos de seguridad.
Dentro de la implementación de cifrado simétrico, destacamos el cifrado 3DES y Rijndael (también conocido como AES), con longitud de clave de cifrado variable.
Ambos son sistemas de cifrado en bloque con una implementación sencilla y excelente velocidad de cálculo, y aunque nos pongan al máximo la CPU, proporcionarán una buena fortaleza en el cifrado (lo suficiente para que desista cualquiera que no disponga de varias supercomputadoras durante varios años)
Para realizar nuestra implementación necesitaremos dos argumentos, la clave de cifrado y el vector de inicialización del algoritmo (o IV, de Inicialization Vector).
Lógicamente, ambos deben ser iguales al cifrar y al descifrar, lo cual no es un problema trivial como veremos en los comentarios del siguiente segmento de código
Imports System Imports System.IO Imports System.Text Imports System.Security.Cryptography ‘ Genera un vector de inicialización para 3DES a partir de la clave de encriptacion Public function generateIV(key as byte ()) As byte () Dim slt(0) As Byte ‘Inicializamos con un byte a 0 para ‘garantizar que el cifrado será reversible. Es posible ‘utilizar otros valores, pero darán vectores de inicializacion diferentes ‘para cada inicializacion Dim sKey As string =System.Text.Encoding.ASCII.String (key) Dim pdb As New PasswordDeriveBytes(key, slt) return pdb.GetBytes(IVlength) End Property
Una vez tengamos la función generateIV, podemos encriptar y desencriptar con 3DES
Public Function encode(ByVal inBytes As Byte (), encodingKey As Byte ()) As Byte () Dim DES As New TripleDESCryptoServiceProvider Dim xIV as Byte ()=generateIV(encodingKey) Dim encryptor As ICryptoTransform = DES.CreateEncryptor(encodingKey, xIV) Try Dim msEncrypt As New MemoryStream Dim csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write) Dim toEncrypt() As Byte toEncrypt = bytes csEncrypt.Write(toEncrypt, 0, toEncrypt.Length) csEncrypt.FlushFinalBlock() Dim encoded As Byte () = msEncrypt.ToArray() Return encoded Catch ex As Exception Return Nothing End Try End Function
Public Function decode(ByVal bytes As Byte (), encodingKey As Byte ()) As Byte () Dim DES As New TripleDESCryptoServiceProvider Dim xIV as Byte ()=generateIV(encodingKey) Dim decryptor As ICryptoTransform = DES.CreateDecryptor(encodingKey, xIV) Try Dim msDecrypt As New MemoryStream(bytes) Dim csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read) Dim fromEncrypt() As Byte fromEncrypt = New Byte (bytes.Length) {} csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length) Return fromEncrypt Catch ex As Exception Return Nothing End Try End Function
Implementando estas funciones en una clase, podremos realizar fácilmente rápidas encriptaciones de datos, sin olvidar que el algoritmo utilizado no nos permitirá enviar la información encriptada a otro usuario sin tener que revelar (y comprometer) nuestra clave.
Rijndael, más conocido como AES, incorpora una longitud de clave mayor (32 bytes), por lo que su resistencia es también mayor. Su implementación es extremadamente similar a ésta manteniendo este esquema general, ya que TripleDESCryptoServiceProvider y RijndaelManaged implementan igualmente el método CreateDecryptor, siendo necesario únicamente cambiar las declaraciones de la primera línea de cada funcion (encode y decode)
El cifrado asimétrico (RSA) nació como solución al problema del intercambio de claves necesario para los cifrados reversibles.
En este tipo de cifrado, existe un directorio de claves públicas (o claves para cifrar), a las que cualquiera puede tener acceso. La ventaja es que esto no supone una vulnerabilidad, ya que con esta clave sólo podemos cifrar.
El propietario de la clave pública tiene a su vez una clave privada, que es la que utiliza para descifrar la información (por otro lado, volvemos al viejo problema de almacenar la clave).
Sólo nos presenta tres inconvenientes, más o menos graves:
Como solución a las limitaciones de RSA se plantes el uso de la envoltura digital
Dadas las limitaciones de, por un lado, el cifrado de clave privada y por otro, el de clave pública, parece que no tenemos una buena solución para intercambiar información de manera segura.
Sin embargo, analizando las ventajas y los inconvenientes de cada método:
Cifrado simétrico
Ventajas | Velocidad Fortaleza de cifrado |
Inconvenientes | No aptos para intercambio de información, ya que no podemos asegurar la seguridad del canal de envío |
Algoritmos | 3DES Rijndael (AES) |
Cifrado asimétrico
Ventajas | Clave pública para cifrado y clave privada para descifrado |
Inconvenientes | Velocidad de encriptación lenta Sólo podemos cifrar 512 bytes como máximo |
Algoritmos | RSA |
Llegamos a la conclusión siguiente, que es lo que se ha llamado “envoltura digital”:
Dadas las ventajas y los inconvenientes de cada método, haremos lo siguiente:
Una vez que sabemos cómo realizar el intercambio de información, veamos cómo implementar el RSA con .Net.
Primero tendremos que implementar una función que nos genere, a partir del algoritmo RSA, una clave privada que deberemos guardar externamente y de la manera más segura posible.
Public Function createRSA() As String Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider(2048) ‘Genera una instancia del algoritmo de cifrado RSA de 2048 bytes y la exporta a ‘formato XML. Dim tempString As String = rsa.ToXmlString(True ) rsa.Clear() Return tempString End Function
¿Para qué hemos hecho esto? Es más o menos sencillo, esta función nos ha exportado la clave privada del RSA en formato XML. A partir de esta clave almacenada, podremos invocar el algoritmo RSA para que nos descifre una cadena de bytes cifrada con la clave pública.
Para recuperar la clave pública sólo tenemos que pasar como argumento la privada (strPrivateKey) en nuestro código
Function getPublicKey(ByVal strPrivatekey As String ) As String Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider ‘ Mandamos reconstruir el RSA original a partir de la clave privada rsa.FromXmlString(strPrivatekey) Return rsa.ToXmlString(False ) End Function
También podemos optar por la opción "Todo en uno"
Public Function createRSA_Pair() As String () Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider(2048) 'Genera una instancia del algoritmo de cifrado RSA de 2048 bytes y la exporta a 'formato XML. Dim publicKey As String = rsa.ToXmlString(False ) Dim privateKey As String = rsa.ToXmlString(True ) Dim RSApair As String ()={privateKey, publicKey} rsa.Clear() Return RSApair End Function
Es importante destacar que, en la llamada al método ToXmlString podemos mandar un booleano si será
Ahora sólo tendremos que enviar a quien desea enviarnos un archivo cifrado nuestra clave pública de RSA, o bien mantener un directorio con la información de todos los usuarios publicada.
Para cifrar una secuencia de bytes, a partir de la clave pública
Function Encrypt(ByVal bPlain As Byte (), ByVal publicKey As String ) As Byte () 'Si bPlain tiene más bytes del número permitido por el tamaño del 'generado, dará un error en tiempo de ejecucion Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider rsa.FromXmlString(publicKey) Dim bCypher As Byte () = rsa.Encrypt(bPlain, False ) Return bCypher End Function
Para descifrar la secuencia de bytes, recuperaremos nuestra clave privada y la pasaremos como parámetro
Function Decrypt(ByVal bCyphered As Byte (), ByVal privateKey As String ) As Byte () Try Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider rsa.FromXmlString(privateKey) Dim bPlain As Byte () = rsa.Decrypt(bCyphered, False ) Return bPlain Catch ex As System.Exception Return Nothing End Try End Function
En otras ocasiones la información no tiene porqué estar cifrada, pero sí es necesario que el destinatario tenga la certeza de que le ha llegado sin alteraciones.
Para eso utilizaremos la firma digital de la información, que podemos considerar como un resumen o cadena de caracteres asociada de manera única a un texto o conjunto de bytes.
Quiere esto decir que, si alteramos tan sólo un byte o carácter de la información, ésta producirá un resumen (HASH) totalmente distinto, por lo que podemos:
Con lo que agregamos integridad a nuestra información.
Adicionalmente, al firmarse cada documento con una clave distinta, podemos asociar a un documento y a una firma a la clave pública con la que se generó, agregando entonces autenticidad al mensaje.
El proceso para realizar una firma digital con .Net es sencillo, primero creamos una función para generar los pares de clave DSA (Digital Signature Algorithm), luego sólo hace falta reconstruir una implementación de DSA a partir de las clave privada y un cifrado HASH posterior con SHA
Public Function generateDSA() As String () Dim DSA As New System.Security.Cryptography.DSACryptoServiceProvider(512) '512 determina la longitud de la clave de encriptación, admite hasta 1024 en 'incrementos de 64, pero ya conocemos el compromiso entre rendimiento y 'seguridad Dim publicKey As String = DSA.ToXmlString(False ) Dim privateKey As String = DSA.ToXmlString(True ) Dim DSAparams As String () = {privateKey, publicKey} Return DSAparams End Function
Como siempre, deberemos tener sumo cuidado con donde almacenamos la clave privada de DSA, ya que cualquiera que la conozca podrá firmar los documentos como si fuéramos nosotros.
Public Function signBytes(ByVal b As Byte (), privateDSA As string ) As Byte () ' Devuelve true si la firma coincide con el contenido en bytes de b Dim DSA As New System.Security.Cryptography.DSACryptoServiceProvider DSA.FromXmlString(privateDSA) Dim signature As New System.Security.Cryptography.DSASignatureFormatter(privateDSA) signature.SetHashAlgorithm("SHA1" ) Dim SHA1 As New System.Security.Cryptography.SHA1Managed Dim bHASH As Byte () = SHA1.ComputeHash(b) Return bHASH End Function
Con esta función obtendremos el resumen HASH de la firma, que enviaremos al destinatario junto con la información que debe ser verificada. No olvidemos, de manera parecida a cuando trabajábamos con RSA mandar la clave pública de nuestro DSA.
Publicando un directorio de usuario-clave pública podremos obtener la autenticidad del mensaje.
Por otro lado, cuando recibamos la información podemos verificarla con la función verifySignture, según describimos a continuación
Public Function verifySignature(ByVal originalBytes As Byte (), publicDSA As String ) As bolean Dim DSA As New System.Security.Cryptography.DSACryptoServiceProvider Dim DSAbytes(DSAmaxBytes) As Byte DSA.FromXmlString(publicDSA) Dim signature As New System.Security.Cryptography.DSASignatureDeformatter(DSA) signature.SetHashAlgorithm("SHA1" ) Dim SHA1 As New System.Security.Cryptography.SHA1Managed Dim bHASH As Byte () = SHA1.ComputeHash(originalBytes) Return signature.VerifySignature(bHASH, bSignature) End Function
Existen otros múltiples objetos para el cifrado en System.Security.Cryptography, pero para empezar y familiarizarnos es suficiente con conocer éstos, ya que nos permitirá resolver casi cualquier problema basado en las tendencias actuales en criptografía.
© 1.997- 2.007 - La Güeb de Joaquín |
Certifico que ésta página está realizada con electrones totalmente reciclables |