Posteado por: fzapataramirez | Jueves, julio 2, 2009

Serialización de datos en C#

Quizás muchos desarrolladores hayan escuchado hablar del termino “serializar la información” y no saber de que se trata. Pues bien, resulta que la serialización de datos no es otra cosa que transformar los datos de tal manera que pueda ser transferida por un canal (Internet, archivo plano, memoria, etc) a otro sistema. En otras palabras, si queremos compartir información de nuestro sistemas con otras aplicaciones o viceversa, tendremos que utilizar serialización de datos.

De manera nativa, el Framework de .NET nos ofrece la posibilidad de serializar la información en tres formatos: Binario, Soap, Xml.

La serialización en formato binario consiste en convertir la información a bytes y se utiliza comúnmente para los escenarios donde la información es transferida por la red hacia un sistema destino, el cual recibe dicha información y realiza el proceso inverso de la serialización = Deserializacion para construir el objeto (información) que fue transferido.
La serialización en formato Soap consiste en convertir los datos en un “documento estándar” en el cual se incluirá además de los datos a serializar, una serie de información adicional que será utilizada por el sistemas destino para construir el objeto original. Esta serialización es la que se utiliza en escenarios con Web Services.
Finalmente la serialización en formato Xml, consiste en transformar la información en un documento Xml que será interpretado por el sistema destino.

Los formatos de serialización Binario y Soap están contenidos en el namespace System.Runtime.Serialization, mientras que el Xml esta en el namespace System.Xml.Serialization.

Para ejemplificar cada escenario, supóngase que en nuestra aplicación se utiliza una entidad llamada Empleado definida como se muestra a continuación:

public class Empleado
{
    private int identificacion;
    private string nombre;
    private string apellido;
    private int edad;
    private string telefono;
    private DateTime fechaIngreso;
    private int diasLaborados;

    public int Identificacion
    {
        get
        {
            return identificacion;
        }
        set
        {
            identificacion = value;
        }
    }
    public string Nombre
    {
        get { return nombre; }
        set { nombre = value; }
    }

    public string Apellido
    {
        get { return apellido; }
        set { apellido = value; }
    }
    public int Edad
    {
        get { return edad; }
        set { edad = value; }
    }
    public DateTime FechaIngreso
    {
        get
        {
            return fechaIngreso;
        }
        set
        {
            fechaIngreso = value;
            DiasLaborados = (DateTime.Now.Subtract(fechaIngreso).Days);
        }
    }
    public string Telefono
    {
        get { return telefono; }
        set { telefono = value; }
    } 
    public int DiasLaborados
    {
        get
        {
            return diasLaborados;
        }
        set
        {
            diasLaborados = value;
        }
    }

}

Serialización en formato Binario
Como se mencionó anteriormente, éste método se utiliza generalmente para intercambiar información con otros sistemas a través de la red. Sin embargo para agilidad en la ilustración de su funcionamiento, los datos serializados en este post, se enviarán a un archivo de texto para visualizar su contenido. Esta serialización tiene la desventaja que solo funcionará entre aplicaciones .NET, es decir, que tanto la aplicación que serializa como la aplicación que deserializa deben ser aplicaciones desarrolladas bajo la plataforma .NET.

El primer paso para serializar un objeto en .NET, es incluir el atributo Serializable en la definición de nuestra clase así:

[Serializable]
public class Empleado
{…]

Esto le indicará al runtime de .NET que este objeto estará habilitado para ser serializado cuando se requiera, de lo contrario, se generará una excepción del tipo System.Runtime.Serialization.SerializationException.

Luego de esto, implementar la serialización binaria es tan sencillo como invocar el método Serialize de la clase BinaryFormatter que encontramos en el namespace System.Runtime.Serialization. Este método recibe como parámetros un Stream y el objeto que deseamos serializar, por ejemplo:

image

Si abrimos el archivo datoSerializado.txt, obtenemos:

image

Como se ve en la figura, la información obtenida después del proceso de serialización contiene una serie de caracteres especiales. Esto es porque en realidad estamos serializando en formato binario y el bloc de notas no esta en capacidad de interpretar este tipo de información. Sin embargo, en la vida real, se supone que ese stream es enviado a través de la red y recibido por una aplicación destino, la cual utilizará el método Deserialize del objeto BinaryReader para obtener el objeto Empleado que fue enviado inicialmente, así:

image

Lo cual da como resultado lo siguiente:

image

Profundizando un poco más en el como el runtime de .NET serializa los datos, debemos saber que en tiempo de ejecución el runtime convierte en bytes cada uno de los miembros del objeto sin importar el tipo o nivel de acceso (public, private, protected, etc). Esto en algunas ocasiones puede no ser lo que deseemos hacer, sino mas bien omitir algunos campos que consideramos no son necesarios al momento de serializar. En nuestro ejemplo especifico, para que queremos serializar el campo díasLaborados si puede ser calculado en tiempo de ejecución?. Esto implica un costo que podemos evitar ya que a mayor cantidad de miembros a serializar, mayor cantidad de información tendrá que ser transferida por red o almacenada en disco.

Para modificar el comportamiento de la serialización binaria, podemos utilizar el atributo NonSerialized en cada uno de los miembros que deseamos omitir, así:

[NonSerialized]
private int diasLaborados;

Con esto logramos que el resultado de la serialización sea el siguiente:

image

Donde se puede ver que ya no existe ninguna información referente al miembro diasLaborados. Sin embargo, al momento de ejecutar la aplicación destino y deserializar el objeto, obtenemos lo siguiente:

image 
Donde podemos observar que no se está calculando el valor del campo diasLaborados. Esto se debe a que el proceso de deserialización lo que hace es “recrear” el estado del objeto serializado y como el campo diasLaborados no está incluido en la información serializada, es omitido en el proceso de deserialización.

Por fortuna, existe la interfaz IDeserializationCallback, la cual incluye el método OnDeserialization que es llamado una vez termina el proceso de deserialización del objeto. Implementar esta interfaz solucionará nuestro problema ya que en dicho método podemos calcular el valor del miembro diasLaborados y así garantizamos que siempre que se realice una deserialización del objeto, se tendrá el estado completo del mismo. Para implementar la interfaz, se hace lo siguiente sobre el objeto Empleado:

[Serializable]
public class Empleado:IDeserializationCallback
{
     //Definicon de miembros

     #region IDeserializationCallback Members

        public void OnDeserialization(object sender)
        {
            DiasLaborados = (DateTime.Now.Subtract(fechaIngreso).Days);
        }

        #endregion

}

Así, ejecutando nuevamente el código que se encarga de realizar la deserialización tenemos:
 image

Serialización en formato SOAP.

La serialización en formato SOAP, consiste en utilizar un formateador especial que genera un documento SOAP con la información del objeto que deseamos serializar. Este formato de serialización es el utilizado en los servicios Web por su gran interoperabilidad entre sistemas ya que me permite tener los sistemas desarrollados bajo diferentes plataformas e incluso ejecutándose en diferentes plataformas (Linux, Windows, etc). Sin embargo, una de las desventajas que tiene este formato de serialización es que genera una gran cantidad de información al serializar nuestro objeto (debido a que se debe cumplir con el estándar de los documentos SOAP). Por este motivo, es necesario analizar que tipo de serialización utilizar en nuestra aplicación. Así entonces tenemos que la serialización binaria es la más eficiente pero sólo nos sirve para escenarios donde las aplicaciones están desarrolladas en .NET. Si por el contrario, necesitamos compartir nuestros datos con aplicaciones desarrolladas en otros lenguajes y además cumplir con el estándar SOAP, utilizaremos el formato SOAP y finalmente si lo que necesitamos es compartir información con aplicaciones desarrolladas en otros lenguajes pero no es necesario cumplir con ningún estándar, podemos utilizar el formato XML.

Para serializar en formato SOAP, sólo se debe utilizar el formateador SoapFormatter que se encuentra en el namespace System.Runtime.Serialization.Formatters.Soap, así:

image

Después de serializar obtenemos el siguiente resultado:

image 
Como se ve en la figura, el comportamiento de la serialización en formato SOAP también se ve afectada por el uso del atributo NonSerialized

Serialización en formato XML.

La serialización en formato XML consiste en transformar el objeto en un documento XML, donde por defecto, el nodo raíz será el tipo de dato que estemos serializando y sus miembros serán elementos de ese nodo raíz.
La implementación de la serialización XML difiere un poco de las presentadas anteriormente ya que no se utiliza un formateador sino que se hace utilizando la clase XmlSerializer que se encuentra en el namespace System.Xml.Serialization. Esta clase recibe como parámetro el tipo de dato que deseamos serializar. Por ejemplo:

image 
De esta manera le estamos indicando al objeto XmlSerializer que deseamos serializar un objeto de tipo Empleado, el cual al momento de invocar el método Serialize, genera un documento XML como el siguiente:

image

Como se puede ver en la figura anterior, el miembro diasLaborados fue serializado. Esto es porque la serialización XML no se ve afectada por el atributo NonSerialized. Sin embargo, la interface IDeserializationCallback si aplica para el proceso de la deserialización del objeto.

Para modificar el comportamiento de la serialización XML, se debe usar el atributo XmlIgnore sobre el miembro que deseamos omitir, así:

[XmlIgnore]
        public int DiasLaborados
        {
            get
            {
                return diasLaborados;
            }
            set
            {
                diasLaborados = value;
            }
        }

Es de notar que el atributo se ha utilizado sobre la propiedad DiasLaborados y no sobre el miembro diasLaborados. Esto es porque la serialización XML serializa únicamente los miembros públicos del objeto, por lo cual si usamos el atributo XmlIgnore sobre el miembro privado diasLaborados, el XmlSerializer igual seguiría generando el elemento DiasLaborados en el documento XML.

En el namespace System.Xml.Serialization, existen varios atributos que permiten modificar el comportamiento de la serialización XML y así obtener diferentes estructuras del documento XML generado. Uno de los atributos más usado es el XmlAttribute el cual se usa sobre cualquier miembro público y sirve para indicarle al runtime que dicho miembro lo genere como un atributo del nodo raíz y no como un elemento. Por ejemplo:

[XmlAttribute]
        public int Identificacion
        {
            get
            {
                return identificacion;
            }
            set
            {
                identificacion = value;
            }
        }
Luego de serializar nuevamente el objeto con el código ilustrado anteriormente, obtenemos:

image

Donde se ve claramente que el miembro Identificación es un atributo del nodo Empleado y no un elemento de dicho nodo como se había generado en el ejemplo previo.

Posteado por: fzapataramirez | Jueves, marzo 19, 2009

Introducción a la criptografía

 

Una de las tareas mas habituales a la hora de proteger información, es sin duda alguna el cifrado de datos. Mediante esta técnica podemos incrementar el nivel de seguridad de los datos que maneja nuestra aplicación, estableciendo un control sobre quien puede leer y/o modificar dicha información.

Hay que tener presente que la criptografía no evita el que las personas que no deben, accedan a la información que tratamos de proteger, sino que evita el entendimiento de la misma, que a la final es lo que nos importa ya que de “nada” le sirve al atacante acceder a las contraseñas de los usuarios de mi sistema si estas contienen un conjunto de caracteres “incoherentes”.

Por ejemplo, digamos que se tiene la cadena “894658987” la cual representa el número de la tarjeta de crédito de un usuario de mi sistema. Al ser esto un dato crítico, considero que debería mantenerlo cifrado en mi base de datos para evitar que un atacante tenga acceso a esta información en caso de que logre vulnerar mi servidor de base de datos. Para esto, se aplica un algoritmo de cifrado mediante el cual se obtiene una cadena “incoherente” para todo aquel que no haga el proceso de descifrado. Para este caso, la cadena cifrada sería algo como Y=?)(/&%ttr6%.

La única manera de obtener la cadena original es aplicando el proceso de descifrado con el mismo algoritmo que se utilizó para cifrar la cadena. La ventaja de todo esto, es que no cualquiera puede descifrar la cadena ya que en los algoritmos criptográficos utilizan llaves (equivalentes a una contraseña) para cifrar y se descifrar y además se garantiza que solo con la llave correcta se puede descifrar. Esto es algo parecido a los procesos de autenticación por contraseña, donde la seguridad está basada en la privacidad de la contraseña. Del mismo modo, la seguridad del cifrado radica en la privacidad de la llave porque todo el que la tenga, podrá acceder a la cadena original.

Pero que es una llave?. Una llave no es más que un conjunto de bytes que cumplen ciertas condiciones según el algoritmo. Uno de los aspectos más importantes de las llaves es su longitud. La longitud de una llave se mide generalmente en bits y se determina según el algoritmo de cifrado utilizado. Por ejemplo el algoritmo DES utiliza llaves de 56 bits. Gran parte de la seguridad de las llaves radica en su longitud debido a que entre más pequeña sea, más fácil será obtener la combinación de bytes mediante fuerza bruta. Por esta razón, quizás la longitud de las llaves utilizadas, sea un factor para elegir el algoritmo de cifrado a utilizar en nuestra aplicación.

Otro de los factores que influye a la hora de elegir el algoritmo adecuado, es el tipo de criptografía que se utiliza: Publica (Asimétrica) o Privada (Simétrica). Ambas son técnicas que difieren en cómo se utiliza la o las llaves de cifrado. A continuación se dará una breve explicación sobre cada una.

Cifrado Simétrico (Privado) 

El cifrado simétrico, utiliza una llave para cifrar y descifrar el mensaje, es decir, se utiliza exactamente la misma llave tanto para cifrar como para descifrar. Esto implica que la llave deberá ser compartida entre todos los que deben acceder a los datos cifrados, lo cual implica un riesgo de seguridad ya que si por ejemplo se transfiere la llave por la red (correo electrónico por ejemplo), existe la posibilidad de que una persona no deseada tenga acceso a la llave y pueda acceder a la información confidencial cifrada.

El proceso consiste entonces en emplear uno de los algoritmos de cifrado simétrico existentes (DES, AES, 3DES, etc) con una llave especifica. Después de aplicar el algoritmo se obtendrá la cadena cifrada que podrá ser transmitida por la red o almacenada en algún lugar específico. Luego cuando sea necesario conocer la cadena original, el destinatario (aplicación, servidor, persona, etc.) deberá usar el mismo algoritmo y la misma llave que se utilizaron para cifrar el mensaje. Únicamente, aquellos remitentes que tengan la llave, podrán descifrar el mensaje.

Cifrado Asimétrico (Público) 

La técnica de cifrado asimétrico no es una mejora de la técnica anterior, es más, no es posible decir que el cifrado asimétrico es mejor que el simétrico ni viceversa. La técnica de cifrado asimétrico, nació para atacar nuevas necesidades.

A lo que voy es que uno de los puntos que ataca el cifrado asimétrico es el hecho de tener que compartir la llave secreta y está pensado para facilitar la comunicación entre un remitente y un destinatario sin necesidad de correr un riesgo a la hora de compartir la llave.

 El cifrado asimétrico usa un total de dos llaves para cifrar los mensajes. Una llave privada y una llave pública. Como sus nombres lo indican, la llave privada es personal y no deberá ser divulgada bajo ninguna circunstancia (ni siquiera a los destinatarios con los que deseo comunicarme), mientras que la llave publica podrá ser publicada y divulgada.

El secreto de esta técnica radica en que todo lo que se cifre con la llave privada, solo podrá ser descifrado con la llave pública y todo lo que se cifre con la llave pública sólo podrá ser descifrado con la llave privada. Esto implica entonces que lo que yo cifre con mi llave privada, podrá ser interpretado por todo aquel que tenga mi llave pública (que en teoría es todo el mundo porque es pública y generalmente se comparte en un servidor Web por ejemplo). De este modo el destinatario solo estaría seguro (en teoría) que el mensaje enviado lo escribí yo y digo que en teoría porque eso no sería cierto en caso de que mi llave privada la tenga otra persona.

En sentido contrario, si el remitente usa la llave publica del destinatario, se podrá garantizar que el mensaje sólo podrá ser descifrado por el destinatario ya que sólo él tiene su propia llave privada. Esto evita entonces el riesgo que implica el tener que compartir una llave que es privada como se tiene que hacer en el cifrado simétrico ya que lo que se comparte en este caso en una llave publica que no hay ningún peligro de que se conozca.

Lo que hay que tener presente en esta técnica, es que si yo deseo comunicarme con un destinatario siempre debo usar la llave pública del destinatario para garantizar que solo él podrá entender el mensaje. Si yo firmara el mensaje con mi llave privada, lo podría descifrar todo el que tenga mi llave pública, lo cual no garantizaría privacidad de los datos sino que le garantizaría al destinatario que yo fui quien escribí el mensaje (esta es la base de las firmas digitales).

El algoritmo adecuado para usar en nuestras aplicaciones va de acuerdo a las necesidades de cada quien. Si se pretende cifrar información que solo será descifrada por la misma aplicación o por otras pocas, es buena idea usar el cifrado simétrico ya que es más rápido y requiere menos procesamiento. Por otro lado, si la información que se cifra es para compartirla por la red o hacia múltiples destinatarios, será buena idea usar cifrado asimétrico para evitar los riesgos de compartir la llave privada.

 

 

 

 

Posteado por: fzapataramirez | Domingo, enero 25, 2009

Excelente oportunidad para administrar tu dinero.

Les recomiendo el sitio www.fmoneymanager.com, el cual es una aplicación Web que te permite gestionar todo lo relacionado con las finanzas personales, incluyendo préstamos, ingresos, egresos, permitiendonos tener un control total sobre como nos gastamos el dinero y en que lo estamos invirtiendo.

Visitenlo, es totalmente gratis y es un proyecto que tiene mucho futuro porque según me enteré, tienen bastantes cosas por ofrecer.

 

Suerte !!!

Older Posts »

Categorías