Request authorization in Azure SDK



Request authorization in Azure SDK


Sergei Meleshchuk – blog


Introduction


 


Azure storage requires all client REST requests be “signed”. The storage server validates the signature. The server will reject the request if it is not properly signed.


The goal of this post is to explain how the client-side signing works. That can help when troubleshooting 403 error codes. In addition, you can sign your requests entirely bypassing the client library of the Azure SDK.


Shared key authentication (authorization) in Azure


 


Client (that is, you) receives a “key”, in a form of Base64 string. The key is sent to you when you sign for Azure services.


The client code takes certain parts of the request in a specific order, and builds (from those parts) a string. I refer to this string as to “signature string”. This string will be the argument to the signing algorithm, the latter computes the “signature”. This computed signature is sent, along with the request, to the server. Needless to say, the shared key itself is not sent.


Thus, the client-side signing procedure is:


          Build a “signature string”, from which the actual signature will be computed,


          Compute signature, using the “signature string” as an argument, and using the shared key


          Send the resulting signature in a form of additional request header.


Building the signature string


By far the most interesting part of the whole signing exercise are rules of building the signature string. I start by listing parts of the request, which go into signature string.


There are two parts.


The first part is the “suffix” of the request URL, which is everything that follows the hostname part of the URL, without the query params starting with “?”.


For example, if the URL is


http://accountname.queue.core.windows.net/queuename/messages?numofmessages=1&timeout=30


Then the suffix part for signature is:


“queuename/messages”.


Part 2 is composed of HTTP request header values, but not all of them. There are two groups of headers that do participate: 1) legacy headers like date, MD5, content type, and 2) Azure-specific headers, which are headers with names starting with “x-ms-”. For example, “x-ms-date”.


For queue and blob storage, the first group can be ignored.


The second group is, in general case, sorted by ascending header name, and concatenated. However, because in all practical cases, we only need one header (which is “x-ms-date”), I ignore the multi-header case here. The gain is extreeme simplicity of the resulting code.


The signature string needs the value of current time, in UTC, and in standard format. The time  is to be retrieved by the code:


string date = DateTime.UtcNow.ToString(


    “R”,


    CultureInfo.InvariantCulture);


 


Now, here is the code that builds the signature string:


string signPart1 = string.Format(CultureInfo.InvariantCulture,


    “{0}\n{1}\n{2}\n{3}\n{4}\n/”,


    method,                 // 0 – HTTP Method, like GET


    string.Empty,           // 1 – “Content-MD5” header value. Leave blank.


    string.Empty,           // 2 – Content type, like MIME. Leave blank.


    string.Empty,           // 3 – Legacy “date” header. Leave blank.


    “x-ms-date:” + date);   // 4 – Azure form of date header.


 


string signPart2 = accountName + “/” + suffix;


string signatureString = signPart1 + signPart2;


 


The resulting signature string looks like below.  


“GET\n\n\n\nx-ms-date:Mon, 01 Dec 2008 05:17:57 GMT\n/accountname/queuename/messages”


Building the signature from signature string


 


This is easy part. All the needed code is:


byte[] signBytes = System.Text.Encoding.UTF8.GetBytes(signatureString);


byte[] hash = new HMACSHA256(sharedKey).ComputeHash(signBytes);


string signature = System.Convert.ToBase64String(hash);


return signature;


 


The first line converts the signature string to byte array; this is needed because the actual hash-computing code works on byte arrays.


The second line computes the hash.


And the third and the last line just convert the signature to Base64.


Note: the “sharedKey” used in line 2 is computed, once, from the Base64 shared key your received by the code like below, also straightforward:


public byte[] Key


{


    get { return Convert.FromBase64String(Base64Key); }


}


How do we actually sign the HTTP request


 


Azure SDK uses an Azure-specific HTTP header, with the name “Authorization”. Don’t be confused by “authorization” vs. “authentication” – the actual use as per now is for authentication of the client. And authentication/authorization header value is computed as below:


string AuthHeaderValue = string.Format(CultureInfo.InvariantCulture,


    “SharedKey {0}:{1}”,


    accountName,


    signature);


Now we all we need to build needed headers for signed HTTP REST request:


request.Headers.Add(“x-ms-date”, date);


request.Headers.Add(“Authorization”, AuthHeaderValue);


 


The End

Comments (2)

  1. David says:

    It's a bad idea to use the Authorization header, it gets stripped by some proxies….

    Causing this error: (This error is wrong from the server side, it should say missing Authentication header):

    HTTP/1.0 505 HTTP Version not supported

    Content-Length: 297

    Content-Type: application/xml

    Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0

    x-ms-request-id: 8a0787ec-7819-4238-98f7-c5c81ad4d641

    x-ms-version: 2009-09-19

    Date: Fri, 26 Nov 2010 07:44:37 GMT

    X-Cache: MISS from cache6-rba.isdsl.net

    Connection: close

    …<?xml version="1.0" encoding="utf-8"?><Error><Code>UnsupportedHttpVersion</Code><Message>The HTTP version specified is not supported for this operation by the server.

    RequestId:8a0787ec-7819-4238-98f7-c5c81ad4d641

    Time:2010-11-26T07:44:37.2170676Z</Message><Version>1.0</Version><Via /></Error>