Cifrar archivos con Azure Key Vault antes de subirlos a Azure Storage

La semana pasada estuve desarrollando una pequeña prueba de concepto para demostrar lo sencillo que es cifrar archivos con Azure Key Vault antes de subirlos a Azure Storage, con el fin de que, por un lado, puedas almacenar archivos con protección mayor y además tener el control de con qué claves se cifra y descifra el contenido.

En este articulo te quiero contar cómo configurar el entorno para poder incorporarlo en tus desarrollos a través de los siguientes pasos:

Crear cuenta de Azure Key Vault

El primer paso es crear una cuenta de Azure Key Vault, la cual será encargada de almacenar los certificados y claves de tus aplicaciones. Para ello basta con ir al portal de Azure y buscar Key Vault a través de la opción del menú New.

Azure Portal – New – Key Vault

Durante la creación solamente es necesario indicar un nombre para el servicio, la ubicación y el grupo de recursos donde quieres alojar el servicio. Por defecto aparece dado de alta un service principal que básicamente es la cuenta de usuario con la que estás creando el servicio, a la cual se le asignan todos los permisos de operación sobre todas las claves y certificados en la cuenta.

Create Key Vault

Generar una clave

Una vez creada la cuenta lo siguiente que necesitas para poder cifrar tus blobs es una clave. Esta puede ser generada a través del servicio o bien puedes subir una tuya si así lo prefieres. En este ejemplo vamos a solicitarla a Key Vault. Haz clic en el apartado Keys:

Azure Key Vault – Keys

y selecciona la opción Add:

Azure Key Vault – Keys – Add

Mantén las opciones por defecto y dale un nombre a tu clave.

Azure Key Vault – Create a key

Utiliza el botón Refresh para poder verla en el listado. Al seleccionarla verás las diferentes versiones de la misma y, al seleccionar la actual, accederás a sus propiedades. Es importante que guardes el valor de Key Identifier, el cual es necesario para poder recuperar la clave después.

Azure Key Vault – Key Identifier

Dar de alta tu aplicación en Azure Active Directory

Para que una aplicación pueda solicitar claves a Key Vault es necesario que esta esté registrada en el Azure Active Directory en el que está dicho servicio. En este ejemplo voy a utilizar una aplicación en ASP.NET MVC y la voy a subir al servicio Azure App Service de Azure para facilitarme el trabajo.
La forma más sencilla de dar de alta la aplicación en Azure Active Directory es entrando en la web app, hacer clic en la sección Authentication/Authorization:

Azure App Service – Authentication – Authorization

y habilitar App Service Authentication. Selecciona el apartado de Azure Active Directory y en el siguiente blade tendrás la opción de darla de alta. Elige el modo Express y haz clic en OK para aceptar la creación y Save para finalizar los cambios.

Azure Active Directory Settings

Asociar políticas a tu certificado en Azure Key Vault

Con el paso anterior, de forma automática se genera un client Id para tu aplicación dentro de Azure Active Directory. Lo siguiente que necesitas es asignar a dicha aplicación una serie de permisos en Key Vault para poder acceder a los recursos que hay en el servicio. En el blade de tu servicio de Key Vault, apartado Overview, puedes ver una sección que llamada Access policies.

Azure Key Vault – Access policies

Utiliza cualquiera de los dos enlaces para acceder a la sección y haz clic en la opción Add para agregar tu sitio web.

Azure Key Vault – Add New principal

Existen diferentes secciones en las que puedes dar permisos. En este caso, dentro del apartado Key permissions selecciona Key Management Operations – GetCryptographic Operations – Unwrap Key.

Azure Key Vault – Key permissions

Haz clic OK en los dos apartados y pulsa Save para guardar todos los cambios.

Instalar paquetes de nuget

Ya tienes Azure Key Vault configurado y listo para ser usado. Para que tu aplicación pueda hacer uso del mismo es necesario instalar una serie de paquetes de nuget que te permitirán recuperar el token de Azure Active Directory, recuperar la clave de Key Vault y gestionar tu cuenta de Azure Storage. Los paquetes que debes instalar son los siguientes:

  • Microsoft.IdentityModel.Clients.ActiveDirectory
  • Microsoft.Azure.KeyVault.Extensions
  • WindowsAzure.Storage

Por otro lado, en la sección App Settings deberías de manejar estos valores:

<add key="StorageAccount" value="STORAGE_ACCOUNT_CONNECTION_STRING" />
      <add key="clientId" value="YOUR_CLIENT_ID" />
      <add key="clientSecret" value="YOUR_CLIENT_SECRET" />

La cadena de conexión del storage, donde quieres subir los archivos, la puedes encontrar en el apartado Access keys del portal. Para el client id y client secret puedes recuperarlos desde la web app, en el apartado Authentication/Authorization > Azure Active Directory y selecciona el apartado de Manage Application. Se abrirá el blade de la aplicación registrada en Azure Active Directory donde podrás copiar el Application Id (que es el client id) y crear una nueva clave en el apartado Keys, que será tu client secret.

AAD – Registered App – Client ID and Secret ID

Cifrar archivos en Azure Storage usando Key Vault

Lo único que queda es probar que todo lo que hemos ido creando y configurando hasta ahora realmente funciona 🙂 para ello en la aplicación de ASP.NET MVC crea las siguientes acciones:

using Microsoft.Azure.KeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Configuration;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;

namespace EncryptDemo.Controllers
{
    public class HomeController : Controller
    {
        CloudBlobClient blobClient;
        public HomeController()
        {
            var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.AppSettings["StorageAccount"]);
            blobClient = storageAccount.CreateCloudBlobClient();
        }

        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Upload(HttpPostedFileBase file)
        {
            if (file != null && file.ContentLength > 0)
            {
                var fileName = Path.GetFileName(file.FileName);

                var cloudResolver = new KeyVaultKeyResolver(GetToken);
                var rsa = cloudResolver.ResolveKeyAsync("https://ACCOUNT_NAME.vault.azure.net/keys/KEY_NAME/VERSION", CancellationToken.None).GetAwaiter().GetResult();
                var policy = new BlobEncryptionPolicy(rsa, null);
                var options = new BlobRequestOptions() { EncryptionPolicy = policy };

                var container = blobClient.GetContainerReference("encrypted");
                container.CreateIfNotExists();
                var blob = container.GetBlockBlobReference(fileName);
                blob.UploadFromStream(file.InputStream, file.ContentLength, null, options, null);

                return Content(blob.Uri.ToString());
            }

            return RedirectToAction("Index");

        }

        public FileResult Download(string url)
        {
            var cloudResolver = new KeyVaultKeyResolver(GetToken);
            var policy = new BlobEncryptionPolicy(null, cloudResolver);
            var options = new BlobRequestOptions { EncryptionPolicy = policy };

            var blob = blobClient.GetBlobReferenceFromServer(new Uri(url));
            var stream = new MemoryStream();
            blob.DownloadToStream(stream, null, options, null);
            stream.Position = 0;

            return File(stream, blob.Properties.ContentType, blob.Name);
        }

        private async static Task<string> GetToken(string authority, string resource, string scope)
        {
            var authContext = new AuthenticationContext(authority);
            var clientCred = new ClientCredential(
                ConfigurationManager.AppSettings["clientId"],
                ConfigurationManager.AppSettings["clientSecret"]);

            var result = await authContext.AcquireTokenAsync(resource, clientCred);

            if (result == null)
                throw new InvalidOperationException("Failed to obtain the JWT token.");

            return result.AccessToken;
        }
    }
}

Por otro lado, en la vista añade lo siguiente:

@{
    ViewBag.Title = "Home Page";
}



<div class="row">
    @using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        <input type="file" name="file" />
        <input type="submit" value="Upload" />
    }
</div>





<div class="row">
    @using (Html.BeginForm("Download", "Home", FormMethod.Get))
    {
        <input type="text" name="url" />
        <input type="submit" value="Download" />
    }
</div>


Para comprobar que todo funciona correctamente, prueba a subir un archivo. Si utilizas herramientas como Microsoft Azure Storage Explorer y descargas el archivo podrás comprobar que el mismo no es posible abrirlo.

Archivo cifrado con Azure Key Vault – We cant open this file

Sin embargo, si utilizas tu aplicación con el correspondiente descifrado que se realiza en la acción Download podrás recuperar los archivos correctamente.

¡Saludos!