Authenticating Storage Requests Using SharedKeyAuthenticationHandler

 

With the older version of the storage client library (version 1.7) you could sign HttpWebRequests using the SignRequestLite function, and there were several examples on the web of how to do this. The SignRequestLite function has been removed from the 2.0+ versions of the storage client library and so far I have not seen any examples of using the new signing functions.

 

The new SCL uses Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler to sign the request, and Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyTableCanonicalizer (for table storage requests) or Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyCanonicalizer (for blob and queue storage requests) to create the canonicalizer. In the simplest form, this is the code (using the blob canonicalizer) to sign an httpWebRequest object:

 Microsoft.WindowsAzure.Storage.Auth.StorageCredentials credentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(storageAccountName, storageAccountKey);
Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler auth;
Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyCanonicalizer canonicalizer = Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyCanonicalizer.Instance;
auth = new Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler(canonicalizer, credentials, storageAccountName);
auth.SignRequest(httpWebRequest, null);

 

For a more full featured example (you can largely ignore the MakeRequest function since it is just a generic HTTP request/response handler):

 
using System.Net;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Auth.Protocol;
 
        private HttpWebRequest SignRequest(HttpWebRequest request, bool isTable = false)
        {
            // Create a StorageCredentials object with account name and key
            string storageAccountName = "kwillstorage2";
            string storageAccountKey = "S4RUcCvGKBPoFvhyJ0p6Wu0ciJnPTn5+b5MgU5olWhqGABAfvFhMFCbOBSeDgL9VF27TFrYzCQnRHYkbgJgxxg==";
            Microsoft.WindowsAzure.Storage.Auth.StorageCredentials credentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(storageAccountName, storageAccountKey);
 
            // Create the SharedKeyAuthenticationHandler which is used to sign the HttpWebRequest
            Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler auth;
            if (isTable)
            {
                // Tables use SharedKeyTableCanonicalizer along with storage credentials and storage account name
                Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyTableCanonicalizer canonicalizertable = Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyTableCanonicalizer.Instance;
                auth = new Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler(canonicalizertable, credentials, storageAccountName);
                if (request.Headers["MaxDataServiceVersion"] == null)
                {
                    request.Headers.Add("MaxDataServiceVersion", "2.0;NetFx");
                }
            }
            else
            {
                // Blobs and Queues use SharedKeyCanonicalizer
                Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyCanonicalizer canonicalizer = Microsoft.WindowsAzure.Storage.Core.Auth.SharedKeyCanonicalizer.Instance;
                auth = new Microsoft.WindowsAzure.Storage.Auth.Protocol.SharedKeyAuthenticationHandler(canonicalizer, credentials, storageAccountName);
            }
 
            // Sign the request which will add the Authorization header
            auth.SignRequest(request, null);
            return request;
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            HttpWebRequest requestBlob, requestTable;
 
            requestBlob = (HttpWebRequest)HttpWebRequest.Create("https://kwillstorage2.blob.core.windows.net/vsdeploy?restype=container&comp=list");
            requestBlob.Method = "GET";
            requestBlob.Headers.Add("x-ms-version", "2014-02-14");
            SignRequest(requestBlob);
            MakeRequest(requestBlob);
 
            requestTable = (HttpWebRequest)HttpWebRequest.Create("https://kwillstorage2.table.core.windows.net/testtable");
            requestTable.Method = "GET";
            requestTable.Headers.Add("x-ms-version", "2014-02-14");
            SignRequest(requestTable, true);
            MakeRequest(requestTable);
        }
 
        private void MakeRequest(HttpWebRequest request)
        {
            HttpWebResponse response = null;
            System.IO.Stream receiveStream;
            System.IO.StreamReader readStream;
            Encoding encode;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                Console.WriteLine(request.Headers.ToString());
                Console.WriteLine(ex.Message + Environment.NewLine + Environment.NewLine + ex.Response.Headers.ToString());
                try
                {
                    receiveStream = ex.Response.GetResponseStream();
                    encode = System.Text.Encoding.GetEncoding("utf-8");
                    // Pipes the stream to a higher level stream reader with the required encoding format. 
                    readStream = new System.IO.StreamReader(receiveStream, encode);
                    Console.WriteLine(readStream.ReadToEnd());

                    // Releases the resources of the response.
                    response.Close();
                    // Releases the resources of the Stream.
                    readStream.Close();
                }
                catch
                {
                }
                return;
            }
 
            Console.WriteLine(request.Method + " " + request.RequestUri + " " + request.ProtocolVersion + Environment.NewLine + Environment.NewLine);
            Console.WriteLine(request.Headers.ToString());
            Console.WriteLine((int)response.StatusCode + " - " + response.StatusDescription + Environment.NewLine + Environment.NewLine);
            Console.WriteLine(response.Headers + Environment.NewLine + Environment.NewLine);
 
            receiveStream = response.GetResponseStream();
            encode = System.Text.Encoding.GetEncoding("utf-8");
            // Pipes the stream to a higher level stream reader with the required encoding format. 
            readStream = new System.IO.StreamReader(receiveStream, encode);
            Console.WriteLine(readStream.ReadToEnd());
 
            // Releases the resources of the response.
            response.Close();
            // Releases the resources of the Stream.
            readStream.Close();
        }