Cifrado en WebApi

 ·  ☕ 11 min  ·  ✍️ eiximenis

    Nota: Este post ha sido importado de mi blog de geeks.ms. Es posible que algo no se vea del todo "correctamente". En cualquier caso puedes acceder a la versión original aquí

    Muy buenas! El otro día recibí el siguiente correo (a través del formulario de contacto del blog):

    Estoy desarrollando una serie de web apis para transacciones con tarjetas de crédito. Mi problema es que no encuentro la forma de cifrar los datos sensibles como el numero de tarjeta de crédito. con un web service esta claro como hacerlos pero no encuentro la forma en una web api. que me recomendas?

    Es un tema interesante y que da para mucho pero a ver si podemos dar cuatro pinceladas

    Antes de nada lo dicho en este post trata de evitar que alguien que use un sniffer para analizar el tráfico de red pueda ver los datos confidenciales que enviamos. Este post no pretende ni puede ser una solución completa al problema, ya que hay muchas casuísticas que se deben tratar y muchos tipos de ataque a los que debemos responder. Honestamente la solución más sencilla pasa por usar HTTPS. Si usas HTTPS delegas toda la seguridad en el canal. Sin duda es lo más sencillo. No hay que hacer nada especial para poder usar HTTPS en WebApi. Si usas IIS Express para desarrollar (lo habitual con VS) habilitar el soporte para HTTPS está a un click de distancia:

    SNAGHTML5d216779

    Al seleccionar SSL Enabled VS te indica tanto la URL http como la https (con otro puerto claro). Para que todo funcione IIS Express genera un certificado SSL de desarrollo y ya tienes HTTPS habilitado. Por supuesto cuando despliegues en producción deberás desplegar el certificado SSL de producción en IIS.

    Conceptos básicos de cifrado

    Si no quieres usar IIS entonces debes encriptar los datos en el cliente y desencriptarlos en el servidor. Eso indica que debes especificar que método de encriptación se usa… hay muchos pero a grandes rasgos se dividen en dos grandes grupos:

    1. De clave simétrica
    2. De clave asimétrica

    En los métodos de clave simétrica, cliente y servidor comparten una clave. Dicha clave es usada para generar texto cifrado a partir de texto plano… y viceversa. Es decir alguien que conozca la clave puede tanto enviar mensajes cifrados y descifrarlos.

    En los métodos de clave asimétrica cada uno de los actores tiene un par de claves. Ambas claves sirven tanto para cifrar como para descifrar, pero con una particularidad: lo cifrado con una clave debe ser descifrado con la otra y viceversa.

    Los métodos asimétricos tienen a priori una seguridad mayor que el método simétrico. Imagina a alguien llamado Alice que quiera enviar un mensaje a Bob. Con un método simétrico:

    1. Alice cifraría el mensaje con la clave K de encriptación
    2. Bob usaría la misma clave K para descifrarlo
    3. Si Bob quiere enviar una respuesta cifrada a Alice la cifraría usando la misma clave K y Alice lo descifraría usando K.

    El punto débil aquí es el intercambio de claves. Si queremos que un mensaje de Alice a Bob solo pueda ser leído por Bob debemos asegurarnos que la clave K solo sea conocida por Alice y por Bob. Porque si dicha clave K llega a un tercero este podrá leer el mensaje.

    Para evitar este punto entran los sistemas asimétricos:

    1. Bob tiene su par de claves Kpu y Kpr. Bob publica la clave Kpu en su web pero se guarda bajo llave la clave Kpr.
    2. Alice quiere mandarle un mensaje cifrado a Bob. Para ello lo cifra con la clave Kpu de Bob (que está en su web).
    3. Bob recibe el mensaje y lo descifra usando su propia clave Kpr (que tiene guardada bajo llave).
    4. Si Bob quiere responder a Alice puede cifrar su respuesta usando la clave Kpu de Alice (que también está en la web de Alice). Alice puede descifrar el texto usando su propia clave Kpr.

    No hay intercambio de claves. Par enviar algo a Bob se cifra con su clave pública (la Kpu de Bob) y tan solo Bob lo puede descifrar con su Kpr (su clave privada). Los métodos asimétricos son más lentos (tanto para cifrar como para descifrar) que los asimétricos y tienen sus propios problemas: existen ataques sobre la Kpu para intentar averiguar la Kpr asociada, ya que es obvio que tienen alguna relación. Algunos algoritmos asimétricos han sido rotos gracias a que se han encontrado relaciones más o menos obvias entre ambas claves que permiten deducir (o acotar suficientemente el ámbito de búsqueda para permitir un ataque por fuerza bruta) la clave privada al saber la pública.

    Cifrando y descifrando datos

    Bueno… veamos como podemos implementar un cifrado asimétrico usando WebApi. Nos centramos solo en el cifrado de datos (no entraremos en temas de firma digital aunque los principios sean muy parecidos). El algoritmo que usaremos será RSA (pero vamos, hay otros) a través de la clase RSACryptoServerProvider.

    La propia clase se encarga de crear el par de claves necesario y luego pueden usarse los métodos ToXmlString() y FromXmlString() para guardar dichas claves o bien incorporar dichas claves en un RSACryptoServerProvider:

    1. static void Main(string[] args)
    2. {
    3.     var rsa = new RSACryptoServiceProvider();
    4.     var both = rsa.ToXmlString(true);
    5.     var pub = rsa.ToXmlString(false);
    6.     File.WriteAllText("both.xml", both);
    7.     File.WriteAllText("pub.xml", pub);
    8. }

    Dicho código crea dos archivos xml (both.xml y pub.xml). El primero contiene el par de claves (pública y privada) mientras que el segundo contiene tan solo la clase pública. Por supuesto hay mejores maneras de guardar las claves pero para este post eso será suficiente.

    Ahora vamos a crear una aplicación ASP.NET WebApi.

    He generado un par de claves y las he guardado en una clase (he copiado el contenido entero de both.xml en una propiedad CryptoKeys.Both y lo mismo para CryptoKeys.Pub):

    1. class CryptoKeys
    2. {
    3.     internal const string Both = "[CONTENIDO ENTERO DEL FICHERO BOTH.XML]";
    4.     internal const string Pub = "[CONTENIDO ENTERO DEL FICHERO PUB.XML]";
    5. }

    Vale… en un mundo real esto NO estaría hardcodeado pero… ¡estamos en el mundo ideal de los blogs!

    Ahora creamos un controlador WebApi para que nos devuelva la clave pública:

    1. public class KeyController : ApiController
    2. {
    3.     // GET api/values
    4.     public string Get()
    5.     {
    6.         return CryptoKeys.Pub;
    7.     }
    8. }

    Una llamada GET a /api/Key nos permite obtener la clave pública del servidor:

    SNAGHTML5d500782

    Vale… vamos a ver el código del cliente.

    1. static void Main(string[] args)
    2. {
    3.     DoEverythingAsync().Wait();
    4. }
    5.  
    6. private static async Task DoEverythingAsync()
    7. {
    8.     string kpu = null;
    9.     // Obtenemos clave del servidor
    10.     using (var client = new HttpClient())
    11.     {
    12.         client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
    13.         var data = await client.GetAsync("http://localhost:25986/api/key");
    14.         kpu = await data.Content.ReadAsStringAsync();
    15.         kpu = JsonConvert.DeserializeObject<string>(kpu);
    16.     }
    17.  
    18.     var encrypted = EncryptData(kpu, "PRIVATE DATA TO SEND");
    19.     await SendEncryptedDataAsync(encrypted);
    20. }

    Básicamente obtenemos la clave pública (llamando a /api/Key del servidor), encriptamos unos datos y los enviamos. El código de EncryptData es:

    1. private static byte[] EncryptData(string kpu, string data)
    2. {
    3.     var rsa = new RSACryptoServiceProvider();
    4.     rsa.FromXmlString(kpu);
    5.     var bytes = new byte[data.Length * sizeof(char)];
    6.     Buffer.BlockCopy(data.ToCharArray(), , bytes, , bytes.Length);
    7.     var encrypted = rsa.Encrypt(bytes, true);
    8.     return encrypted;
    9. }

    Simplemente usamos el método FromXmlString para inicializar el RSACryptoServiceProvider con la clave pública obtenida previamente. Luego llamamos a Encrypt para encriptar los datos.

    Finalmente el código de SendEncryptedDataAsync:

    1. private static async Task SendEncryptedDataAsync(byte[] encrypted)
    2. {
    3.     using (var client = new HttpClient())
    4.     {
    5.         var content = new FormUrlEncodedContent(new[]
    6.         {
    7.             new KeyValuePair<string, string>("CC", Convert.ToBase64String(encrypted)),
    8.             new KeyValuePair<string, string>("Name", "edu"),
    9.         });
    10.         var result = await client.PostAsync("http://localhost:25986/api/Test", content);
    11.         Console.WriteLine("Status Code: " + result.StatusCode);
    12.     }
    13. }

    Enviamos dos campos en el POST, uno llamado CC (el encriptado) y otro llamado Name que NO está encriptado.

    Hecho esto, en el servidor nos creamos un controlador de prueba (Test):

    1. public class TestController : ApiController
    2. {
    3.     public void Post(CreditCardInfo card)
    4.     {
    5.         
    6.     }
    7. }
    8.  
    9. public class CreditCardInfo
    10. {
    11.     public string Name { get; set; }
    12.     public string CC { get; set; }
    13. }

    Si ahora levantamos la aplicación WebApi y ejecutamos nuestra aplicación de consola cliente vemos que los datos llegan al servidor:

    image

    Por supuesto el servidor tiene que descifrarlos… Veamos como:

    1. public void Post(CreditCardInfo card)
    2. {
    3.     var rsa = new RSACryptoServiceProvider();
    4.     rsa.FromXmlString(CryptoKeys.Both);
    5.     var bytes = Convert.FromBase64String(card.CC);
    6.     var decrypted = rsa.Decrypt(bytes, true);
    7.     var chars = new char[decrypted.Length / sizeof(char)];
    8.     Buffer.BlockCopy(decrypted, , chars, , decrypted.Length);
    9.     var decryptedString = new string(chars);
    10. }

    ¡Al final en la variable decryptedString estarán los datos descifrados!

    Un poco más de integración con WebApi

    Tener que colocar el código de descifrado continuamente no es muy agradecido. Por suerte podemos integrarnos dentro de WebApi de forma relativamente simple.

    WebApi usa media type formatters para pasar los datos que vienen en el cuerpo de la petición http a un parámetro del controlador. En nuestro caso mandamos dos datos (Name y CC). Para saber que media type formatter usar WebApi debe saber en qué formato están los datos en la petición y para ello usa la cabecera content-type. El valor normal de content-type cuando enviamos datos vía POST es application/x-www-form-urlencoded (si enviasemos un JSON usaríamos application/json). WebApi viene con soporte nativo para application/x-www-form-urlencoded a través de dos media type formatters:

    • FormUrlEncodedMediaTypeFormatter: Se usa si el controlador recibe un JToken o un FormCollection
    • JQueryMvcFormUrlEncodedFormatter: Se usa si el controlador recibe un objeto como parámetro. Deriva del anterior.

    En nuestro caso el controlador recibe un CreditCardInfo por lo que será JQueryMvcFormUrlEncodedFormatter el que procese los datos de la petición, y cree el CreditCardInfo que recibe el controlador en el método POST.

    Así la solución pasa por derivar de JQueryMvcFormUrlEncodedFormatter y añadir en la clase derivada el código de descifrado. Veamos como hacerlo de forma sencilla. Primero creamos la clase CypheredFomUrlEncodedMediaTypeFormatter:

    1. public class CypheredFormUrlEncodedMediaTypeFormatter : JQueryMvcFormUrlEncodedFormatter
    2. {
    3.     public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content,
    4.         IFormatterLogger formatterLogger)
    5.     {
    6.         return this.ReadFromStreamAsyncCore(type, readStream, content, formatterLogger);
    7.     }
    8. }

    Sobreescribimos el método ReadFromStreamAsync. Este método debe leer los datos del cuerpo de la petición, crear el objeto del tipo type indicado y pasar los datos de la petición al objeto creado.

    Todo el código lo tendremos en el método ReadFromStreamAsyncCore de la propia clase:

    <div style="background: #ddd; max-height: 300px; overflow: auto">
      <ol start="1" style="background: #1d1d1d; margin: 0 0 0 2.5em; padding: 0 0 0 5px;">
        <li>
          <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">public</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">async</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#4ec9b0">Task</span><span style="background:#1e1e1e;color:#b4b4b4"><</span><span style="background:#1e1e1e;color:#569cd6">object</span><span style="background:#1e1e1e;color:#b4b4b4">></span><span style="background:#1e1e1e;color:#dcdcdc"> ReadFromStreamAsyncCore(</span><span style="background:#1e1e1e;color:#4ec9b0">Type</span><span style="background:#1e1e1e;color:#dcdcdc"> type, </span><span style="background:#1e1e1e;color:#4ec9b0">Stream</span><span style="background:#1e1e1e;color:#dcdcdc"> readStream, </span><span style="background:#1e1e1e;color:#4ec9b0">HttpContent</span><span style="background:#1e1e1e;color:#dcdcdc"> content, </span><span style="background:#1e1e1e;color:#b8d7a3">IFormatterLogger</span><span style="background:#1e1e1e;color:#dcdcdc"> formatterLogger)</span>
        </li>
        <li style="background: #111111">
              <span style="background:#1e1e1e;color:#dcdcdc">{</span>
        </li>
        <li>
                  <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> obj </span><span style="background:#1e1e1e;color:#b4b4b4">=</span>
        </li>
        <li style="background: #111111">
                      <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">await</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">base</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">ReadFromStreamAsync(</span><span style="background:#1e1e1e;color:#569cd6">typeof</span><span style="background:#1e1e1e;color:#dcdcdc">(</span><span style="background:#1e1e1e;color:#4ec9b0">FormDataCollection</span><span style="background:#1e1e1e;color:#dcdcdc">), readStream, content, formatterLogger) </span><span style="background:#1e1e1e;color:#569cd6">as</span>
        </li>
        <li>
                          <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#4ec9b0">FormDataCollection</span><span style="background:#1e1e1e;color:#dcdcdc">;</span>
        </li>
        <li style="background: #111111">
                  <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> binded </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> obj</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">ReadAs(type);</span>
        </li>
        <li>
                  <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">foreach</span><span style="background:#1e1e1e;color:#dcdcdc"> (</span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> property </span><span style="background:#1e1e1e;color:#569cd6">in</span><span style="background:#1e1e1e;color:#dcdcdc"> type</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">GetProperties()</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">Where(p </span><span style="background:#1e1e1e;color:#b4b4b4">=></span><span style="background:#1e1e1e;color:#dcdcdc"> p</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">PropertyType </span><span style="background:#1e1e1e;color:#b4b4b4">==</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">typeof</span><span style="background:#1e1e1e;color:#dcdcdc">(</span><span style="background:#1e1e1e;color:#569cd6">string</span><span style="background:#1e1e1e;color:#dcdcdc">)))</span>
        </li>
        <li style="background: #111111">
                  <span style="background:#1e1e1e;color:#dcdcdc">{</span>
        </li>
        <li>
                      <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> attr </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> property</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">GetCustomAttribute</span><span style="background:#1e1e1e;color:#b4b4b4"><</span><span style="background:#1e1e1e;color:#4ec9b0">CypheredDataAttribute</span><span style="background:#1e1e1e;color:#b4b4b4">></span><span style="background:#1e1e1e;color:#dcdcdc">();</span>
        </li>
        <li style="background: #111111">
                      <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">if</span><span style="background:#1e1e1e;color:#dcdcdc"> (attr </span><span style="background:#1e1e1e;color:#b4b4b4">!=</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">null</span><span style="background:#1e1e1e;color:#dcdcdc">)</span>
        </li>
        <li>
                      <span style="background:#1e1e1e;color:#dcdcdc">{</span>
        </li>
        <li style="background: #111111">
                          <span style="background:#1e1e1e;color:#dcdcdc">property</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">SetValue(binded, Decrypt(property</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">GetValue(binded) </span><span style="background:#1e1e1e;color:#569cd6">as</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">string</span><span style="background:#1e1e1e;color:#dcdcdc">));</span>
        </li>
        <li>
                      <span style="background:#1e1e1e;color:#dcdcdc">}</span>
        </li>
        <li style="background: #111111">
                  <span style="background:#1e1e1e;color:#dcdcdc">}</span>
        </li>
        <li>
          &nbsp;
        </li>
        <li style="background: #111111">
                  <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">return</span><span style="background:#1e1e1e;color:#dcdcdc"> binded;</span>
        </li>
        <li>
              <span style="background:#1e1e1e;color:#dcdcdc">}</span>
        </li>
      </ol>
    </div></p>
    

    El método ReadFromStreamAsyncCore hace lo siguiente:

    1. Usamos el método (de la clase base) ReadFromStreamAsync pasándole como parámetro un FormDataCollection. Con eso obtenemos un FormDataCollection (básicamente un diccionario clave, valor) con los datos del cuerpo de la petición. Eso nos funciona porque derivamos de FormUrlEncodedMediaTypeFormatter que tiene soporte para FormDataCollection.
    2. Usamos el método (de extensión) ReadAs de FormDataCollection que crea un objeto del tipo indicado a partir de los datos de un FormDataCollection. Es decir, hace el binding de propiedades.
    3. Ahora iteramos sobre todas las propiedades de tipo cadena y miramos si alguna tiene el atributo CypheredDataAttribute aplicado.
    4. Si lo tiene desciframos el valor de dicha propiedad mediante una llamada a Decrypt.

    El código del método Decrypt es básicamente el mismo que teníamos antes en el controlador:

    <div style="background: #ddd; max-height: 300px; overflow: auto">
      <ol start="1" style="background: #1d1d1d; margin: 0 0 0 2.5em; padding: 0 0 0 5px;">
        <li>
          <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">private</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">string</span><span style="background:#1e1e1e;color:#dcdcdc"> Decrypt(</span><span style="background:#1e1e1e;color:#569cd6">string</span><span style="background:#1e1e1e;color:#dcdcdc"> encryptedBase64)</span>
        </li>
        <li style="background: #111111">
          <span style="background:#1e1e1e;color:#dcdcdc">{</span>
        </li>
        <li>
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> rsa </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">new</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#4ec9b0">RSACryptoServiceProvider</span><span style="background:#1e1e1e;color:#dcdcdc">();</span>
        </li>
        <li style="background: #111111">
              <span style="background:#1e1e1e;color:#dcdcdc">rsa</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">FromXmlString(</span><span style="background:#1e1e1e;color:#4ec9b0">CryptoKeys</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">Both);</span>
        </li>
        <li>
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> bytes </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#4ec9b0">Convert</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">FromBase64String(encryptedBase64);</span>
        </li>
        <li style="background: #111111">
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> decrypted </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> rsa</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">Decrypt(bytes, </span><span style="background:#1e1e1e;color:#569cd6">true</span><span style="background:#1e1e1e;color:#dcdcdc">);</span>
        </li>
        <li>
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> chars </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">new</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">char</span><span style="background:#1e1e1e;color:#dcdcdc">[decrypted</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">Length </span><span style="background:#1e1e1e;color:#b4b4b4">/</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">sizeof</span><span style="background:#1e1e1e;color:#dcdcdc">(</span><span style="background:#1e1e1e;color:#569cd6">char</span><span style="background:#1e1e1e;color:#dcdcdc">)];</span>
        </li>
        <li style="background: #111111">
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#4ec9b0">Buffer</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">BlockCopy(decrypted, </span><span style="background:#1e1e1e;color:#b5cea8"></span><span style="background:#1e1e1e;color:#dcdcdc">, chars, </span><span style="background:#1e1e1e;color:#b5cea8"></span><span style="background:#1e1e1e;color:#dcdcdc">, decrypted</span><span style="background:#1e1e1e;color:#b4b4b4">.</span><span style="background:#1e1e1e;color:#dcdcdc">Length);</span>
        </li>
        <li>
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">var</span><span style="background:#1e1e1e;color:#dcdcdc"> decryptedString </span><span style="background:#1e1e1e;color:#b4b4b4">=</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">new</span><span style="background:#1e1e1e;color:#dcdcdc"> </span><span style="background:#1e1e1e;color:#569cd6">string</span><span style="background:#1e1e1e;color:#dcdcdc">(chars);</span>
        </li>
        <li style="background: #111111">
              <span style="background:#1e1e1e;color:#dcdcdc"></span><span style="background:#1e1e1e;color:#569cd6">return</span><span style="background:#1e1e1e;color:#dcdcdc"> decryptedString;</span>
        </li>
        <li>
          <span style="background:#1e1e1e;color:#dcdcdc">}</span>
        </li>
      </ol>
    </div></p>
    

    Con esto ya tenemos un media type formatter que descifrará automáticamente todas aquellas propiedades string decoradas con [CypheredData]. El código de CypheredDataAttribute es trivial:

    1. [AttributeUsage(AttributeTargets.Property)]
    2. public class CypheredDataAttribute : Attribute
    3. {
    4. }

    Ahora nuestra clase CreditCardInfo nos queda de la siguiente manera:

    1. public class CreditCardInfo
    2. {
    3.     public string Name { get; set; }
    4.     [CypheredData]
    5.     public string CC { get; set; }
    6. }

    ¡Un último detalle! Tenemos que agregar nuestro MediaTypeFormatter en la configuración de webapi:

    1. config.Formatters.Remove(
    2.     config.Formatters.Single(f => typeof (JQueryMvcFormUrlEncodedFormatter) == f.GetType()));
    3. config.Formatters.Add(new CypheredFormUrlEncodedMediaTypeFormatter());

    Y lo mejor: ¡en el controlador no tenemos que hacer nada para tener los datos descifrados!

    image

    ¡Listos! Hemos visto como podemos enviar datos cifrados y como podemos integrarnos un poco dentro de webapi para que el descifrado sea sencillo. No hemos cubierto todos los casos posibles, pero espero que os haya dado algunas ideas de como implementarlo!

    Saludos!

    Si quieres, puedes invitarme a un café xD

    eiximenis
    ESCRITO POR
    eiximenis
    Compulsive Developer