Cifrado en una Web Api
Hola nuevamente, después de mucho tiempo decido hacer un nuevo post de algo que me ha pasado en mi diario trajinar; bueno al punto, me solicitaron de una empresa X que necesitaban informacion de mi empresa, la sugerencia fue crear una Web Api para que ellos puedan consumir la informacion e implementarla en su requerimiento.
En este punto doy por entendido que todos sabemos realizar una Web Api (ya crearé un post con este tema); mi inconveniente era que, al publicar mi Web Api dejá al descubierto la informacion para todo aquel que sepa la url.
Quiero aclarar que este post no pretende ser una “solución completa” al problema, pero para mi funciona bastante bien.
Conceptos básicos de cifrado
Hay varios métodos de encriptacion, a grandes rasgos podríamos indicar que existen dos tipos:- De clave simétrica
- De clave asimétrica
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:
- Alice cifraría el mensaje con la clave K de encriptación
- Bob usaría la misma clave K para descifrarlo
- Si Bob quiere enviar una respuesta cifrada a Alice la cifraría usando la misma clave K y Alice lo descifraría usando K.
Para evitar este punto entran los sistemas asimétricos:
- 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.
- Alice quiere mandarle un mensaje cifrado a Bob. Para ello lo cifra con la clave Kpu de Bob (que está en su web).
- Bob recibe el mensaje y lo descifra usando su propia clave Kpr (que tiene guardada bajo llave).
- 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.
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:
System.Security.Cryptography.RSACryptoServiceProvider rsacp = new System.Security.Cryptography.RSACryptoServiceProvider(); String clavePrivada = rsacp.ToXmlString(true); String clavePublica = rsacp.ToXmlString(false);
Ahora vamos a la aplicación ASP.NET WebApi.
Este par de claves las he guardado en una clase (he copiado el contenido entero de clavePrivada en una propiedad Token.privada y lo mismo para Token.publica):
class Token { internal const string privada = "[CONTENIDO DE clavePrivada]"; internal const string publica = "[CONTENIDO DE clavePublica]"; }
[HttpGet] public HttpResponseMessage Get() { HttpStatusCode sc = HttpStatusCode.OK; string dato = Aplicacion.Lead.Api.Models.Token.publica; var a = Request.CreateResponse(sc); a.Content = new StringContent(dato, System.Text.Encoding.UTF8, "application/xml"); return a; }
[HttpGet] public HttpResponseMessage Get() { HttpStatusCode sc = HttpStatusCode.OK; string dato = Aplicacion.Lead.Api.Models.Token.publica; var a = Request.CreateResponse(sc); a.Content = new StringContent(dato, System.Text.Encoding.UTF8, "application/xml"); return a; }
Una llamada GET a /api/Key nos permite obtener la clave pública del servidor:
Ahora veamos que se deberia hacer del lado del cliente
string kpu = null; // Obtenemos clave del servidor using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); var data = await client.GetAsync("http://localhost:25986/api/key"); kpu = await data.Content.ReadAsStringAsync(); kpu = JsonConvert.DeserializeObject<string>(kpu); } var encrypted = EncryptData(kpu, "PRIVATE DATA TO SEND"); await SendEncryptedDataAsync(encrypted); }
static void Main(string[] args) { DoEverythingAsync().Wait(); } private static async Task DoEverythingAsync() { string kpu = null; // Obtenemos clave del servidor using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); var data = await client.GetAsync("http://localhost:25986/api/token"); kpu = await data.Content.ReadAsStringAsync(); kpu = JsonConvert.DeserializeObject<string>(kpu); } var encrypted = EncryptData(kpu, "PRIVATE DATA TO SEND"); await SendEncryptedDataAsync(encrypted); }
private static byte[] EncryptData(string kpu, string data) { var rsa = new RSACryptoServiceProvider(); rsa.FromXmlString(kpu); var bytes = new byte[data.Length * sizeof(char)]; Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length); var encrypted = rsa.Encrypt(bytes, true); return encrypted; }
Finalmente el código de SendEncryptedDataAsync:
private static async Task SendEncryptedDataAsync(byte[] encrypted) { using (var client = new HttpClient()) { var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("CC", Convert.ToBase64String(encrypted)), new KeyValuePair<string, string>("Name", "edu"), }); var result = await client.PostAsync("http://localhost:25986/api/Test", content); Console.WriteLine("Status Code: " + result.StatusCode); } }
private static async Task SendEncryptedDataAsync(byte[] encrypted) { using (var client = new HttpClient()) { var content = new FormUrlEncodedContent(new[] { new KeyValuePair<string, string>("CC", Convert.ToBase64String(encrypted)), new KeyValuePair<string, string>("Name", "edu"), }); var result = await client.PostAsync("http://localhost:25986/api/Test", content); Console.WriteLine("Status Code: " + result.StatusCode); } }
Para mi caso en particular lo que deseaba era que el cliente me envíe su “clave de consulta” cifrada. En un inicio mi api estaba de esta forma:
Con los cambios que realice me queda de esta forma
Asi al momento de la consulta debería incluirme el token, en mi Web Api con la clave privada decifrar el contenido y darle paso a la consulta que necesita si es que es correcta la “clave” enviada.
El codigo para descrifrar
private bool ValidarClienteConsulta(string token) { byte[] anotherCrypt = Convert.FromBase64String(token); RSACryptoServiceProvider rsaPrivate = new RSACryptoServiceProvider(); rsaPrivate.FromXmlString(Token.privada); byte[] decryptedRSA = rsaPrivate.Decrypt(anotherCrypt, true); var chars = new char[decryptedRSA.Length / sizeof(char)]; Buffer.BlockCopy(decryptedRSA, 0, chars, 0, decryptedRSA.Length); var decryptedString = new string(chars); if (decryptedString == "[CLAVE ENTREGADA AL USUARIO]") { return true; } else { return false; } }
public Newtonsoft.Json.Linq.JObject Get(string token) { if (ValidarClienteConsulta(token)) { //El codigo aquí } else { return new Newtonsoft.Json.Linq.JObject(); } } public Newtonsoft.Json.Linq.JObject Get([FromUri]string id, string token) { if (ValidarClienteConsulta(token)) { //El codigo aquí } else { return new Newtonsoft.Json.Linq.JObject(); } }
Parte de este contenido fue tomado de https://geeks.ms/etomas/2014/07/01/cifrado-en-webapi/
0 comentarios:
Publicar un comentario
Muchas gracias en cuanto terminemos de revisar el comentario se verá reflejado en el blog