Cómo crear el header Authorization para operaciones REST en Azure Storage?

Avanzado

Lo sé porque lo debo de saber, es mi propósito

Lo sé porque lo debo de saber, es mi propósito

Hola Neo, te hace falta entender con que llave puedes entrar a la Matrix?

Papeles

Neo: Tú

Llave: Authorization Header

Matrix: Azure Storage

Cada vez que vamos a enviar operaciones al storage de Azure debemos llenar el header de autorización, esto en primera instancia parece una tarea fácil de realizar.

De acuerdo a la documentación el header se debe elaborar de la siguiente forma:

 Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"

Revisémoslo por partes.

[SharedKey|SharedKeyLite]

Un error muy común es presumir que SharedKey es la StorageKey que tenemos en nuestra cuenta de storage de Windows Azure, esta llave si tiene que ver en el proceso pero jamás se envía como parte del header.

En la historia de Azure Storage tenemos ya el release de unas cuantas versiones, y con cambios entre una y otra versión han llegado también cambios en la forma de obtener el signature. Es decir ha cambiado el esquema de autenticación.

En el caso de Blob y Queue Storage ahora se debe incluir información adicional al momento de calcular la firma, sin embargo por temas de compatibilidad hacia atrás se permite seguir calculando la firma de la forma como se hacia anteriormente de tal forma que no dejen de funcionar las cosas que ya venían haciéndolo.

Por ello cuando hacemos operaciones con versiones anteriores a 2009-09-19 hacemos uso de SharedKeyLite (llave compartida ligera) que no es sino una versión más sencilla, menos compleja de SharedKey.

En este artículo explicare como se hace utilizando SharedKey que al ser una versión más compleja ya incorpora en si misma los cálculos para de SharedKeyLite.

SharedKeyLite es el esquema usado para utilización de TableStorage aún en la versión 2012-02-12, es decir en el caso de Table Storage no se hace uso de SharedKey.

Así las cosas el encabezado queda de la siguiente manera.

 Authorization="SharedKey <AccountName>:<Signature>"

AccountName

Es el nombre de la cuenta de storage de Windows Azure que se haya creado, por ejemplo MyStorage.

 Authorization="SharedKey MyStorage:<Signature>"

Signature

Tricky part

La firma se utiliza para que tanto el cliente que envía la petición como el servidor de Azure Storage puedan validar que la información recibida es confiable y proviene de una fuente segura.

Así que con la información que existe en el el cliente que realiza la solicitud se realiza una serie de cálculos para obtener una firma, y en el servidor se realizan de nuevo dichos cálculos . Si la firma resultante es la misma entonces la operación es autorizada, de lo contrario es rechazada con un código de error 403 (forbidden) .

La creación de la firma se puede reducir a dos pasos

  • Crear la cadena a firmar
  • Firmar la cadena

Crear la cadena a firmar

De acuerdo a la documentación la cadena a firmar es una cadena donde se concatenan una línea tras otra con

  • La operación a realizar (PUT, GET, POST,DELETE, etc)
  • Una serie de encabezados http pre establecidos, cuando no se tiene alguno de los encabezados se deja la línea en blanco
  • Una serie de encabezados http canónicos, esto es los encabezados adicionados al request que comienzan por x-ms- , deben concatenarse y ordenarse de manera alfabética siguiendo este patrón "x-ms-headername:value\n" , si algún header x-ms- no es utilizado simplemente no hace parte de la lista y no se deja salto de línea en su lugar.
  • El nombre canónico del recurso, implica escribir el recurso que se esta accediendo en formato "/accountName/containers/blobname\parameter:value\n" siendo la última parte una lista con los parámetros utilizados cada uno con formato "parámetro:valor\n" al final de esta cadena NO hay salto de línea.
  • Cuando no se tiene alguno de los encabezados del segundo punto se deja la línea en blanco.

Quedando de esta forma ( los comentarios están solo por claridad , NO hacen parte de la cadena )

 GET\n /*HTTP Verb*/  
\n    /*Content-Encoding*/
\n    /*Content-Language*/
\n    /*Content-Length*/
\n    /*Content-MD5*/
\n    /*Content-Type*/
\n    /*Date*/
\n    /*If-Modified-Since */
\n    /*If-Match*/
\n    /*If-None-Match*/
\n    /*If-Unmodified-Since*/
\n    /*Range*/
x-ms-date:Sun, 11 Oct 2009 21:49:13 GMT\n  
x-ms-version:2009-09-19\n/*CanonicalizedHeaders*/  
/myaccount/mycontainered\n
comp:metadata\n  
restype:container\n  
timeout:20/*CanonicalizedResource*  

Acá esta el extracto de los headers canónicos del ejemplo

 x-ms-date:Sun, 11 Oct 2009 21:49:13 GMT\nx-ms-version:2009-09-19\n

Es decir cumplen con el formato "header:valor\n" y además están organizados alfabéticamente.

Este es un extracto del recurso canónico

 /myaccount/mycontainered\ncomp:metadata\nrestype:container\ntimeout:20 

Nota: en el simulador de Azure Storage el nombre de la cuenta se debe colocar dos veces en el nombre de recurso canónico, ejemplo:

 /myaccount/myaccount/mycontainered\ncomp:metadata\nrestype:container\ntimeout:20 

Si eliminamos los comentarios vemos como queda la cadena finalmente:

 GET\n\n\n\n\n\n\n\n\n\n\n\nx-ms-date:Sun, 11 Oct 2009 21:49:13 GMT\nx-ms-version:2009-09-19\n/myaccount/myaccount/mycontainered\ncomp:metadata\nrestype:container\ntimeout:20

Para tener más claridad respecto a como calcular los siguientes campos

  • Que headers se deben utilizar
  • Date / x-ms-date , que deben estar en formato RFC1123
  • Canonicalized Headers, cuales se deben utilizar
  • les recomiendo revisar este enlace Put Blob (REST API)

Firmar la cadena

Ya teniendo preparada la cadena como lo vimos en el punto anterior se procede a firmarla.

De acuerdo a la documentación se debe hacer utilizando este patrón:

 Signature=Base64(HMAC-SHA256(UTF8(StringToSign)))

Analizándolo de adentro hacia afuera:

 (UTF8(StringToSign)

dado que la misma cadena a nivel binario puede ser completamente diferente lo primero que debemos hacer es convertirla a encoding UTF8. Una vez así ya sabemos que ambos lados firmarán la misma información.

 HMAC-SHA256(UTF8(StringToSign))

Esto indica que la cadena debe firmarse utilizando el algoritmo HMAC-SHA256... solo que en la documentación se ha olvidado decir algo que es SUMAMENTE IMPORTANTE:

  • Para firmar una cadena con dicho algoritmo se requiere una llave de encripción simétrica
  • La llave de encripción simétrica es la CLAVE DE ACCESO PRIMARIA de la cuenta de Azure Storage que se esta utilizando.

Es importante tener en cuenta que la CLAVE DE ACCESO PRIMARIA es entregada por Azure expuesta como una cadena Base64.

Hacer la tarea de firmado cambia de acuerdo al lenguaje y framework que estemos utilizando.

En este artículo muestro como hacerlo fácilmente desde Windows 8 (o sea WinRT), de paso haciendo la conversión al encoding UTF 8.

Cómo usar mecanismos de firma digital en Windows 8? - WinRT

 Base64(HMAC-SHA256(UTF8(StringToSign)))

Los algoritmos de cifrado - hash - signature devuelven la información en forma de información binaria que no es representable por medio de cadenas de texto, y un header de un request http requiere que la información este en texto plano.

Una de las formas de mostrar contenido binario como texto plano es utilizando el esquema de codificación Base64 técnicamente no es una cosa que aprendas a hacer en un par de minutos, pero por fortuna es tan ampliamente adoptado que todos los frameworks ya lo incluyen como funcionalidad, de tal forma que envías un conjunto de datos y obtienes una cadena en dicho formato.

Así las cosas el header de autorización puede lucir de manera muy similar a esto:

 Authorization="SharedKey MyStorage:Kcb0oMaIBCLITcBJStWjNj5WN309FEhbbfIb7ocX3bE="

Ejemplo

En este artículo, tengo un video donde se explica paso por paso como hacer una operación REST para manipular el storage de Windows Azure desde 8 (WinRT)y de paso muestro como elaborar el header Authorization con todo lo expuesto en este artículo incluyendo como realizar la firma digital.