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