Azure auth: an example
i was able to put together a simple example of how to construct a valid authentication header for the Azure Table Storage service. it's a bit convoluted. i'm already on record saying i'm not a fan of this custom auth scheme. but since the folks at MSFT failed to check w/ me first[grin], we'll all have to learn to deal with it.
this solution does not rely on the Azure SDK. the advantage here is that anyone with an HTTP client and a hashing library can use this as a template to start manipulating their Azure storage accounts. no .NET requirements, not OS requirements. just HTTP.
below is the C# code that gets the list of tables in a storage account. to run this, you'll need access to the Azure Storage service. that means you need your project name, endpoint, and Azure primary key. once you plug them into this example, you can run this console app and get an Atom feed back that lists your tables.
using System;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.IO;
using System.Web;
using System.Configuration;
namespace Amundsen.Azure.Storage
{
class Program
{
static void Main(string[] args)
{
// args for this sample
string keyType = "SharedKey";
string method = "GET";
string contentMD5 = string.Empty;
string contentType = "application/atom+xml";
DateTime reqDate = DateTime.UtcNow;
// formatters
string fmtHeader = "{0} {1}:{2}";
string fmtStringToSign = "{0}\n{1}\n{2}\n{3:R}\n{4}";
// get data from config file
string account = GetConfigItem("account"); // your azure project name
string endPoint = GetConfigItem("endPoint"); // the table endpoint created for your azure project
string authKey = GetConfigItem("authKey"); // the primary key created for your azure project
// build request/resource strings
string canonicalResource = string.Format("/{0}/{1}", account, "tables");
string requestUrl = string.Format("{0}/{1}", endPoint, "tables");
// build up auth hash
string authValue = string.Format(fmtStringToSign, method, contentMD5, contentType, reqDate, canonicalResource);
string sigValue = ComputeMacSha(authValue, Convert.FromBase64String(authKey));
string authHeader = string.Format(fmtHeader, keyType, account, sigValue);
// make the request and show response
try
{
string rtn = string.Empty;
WebRequest req = WebRequest.Create(requestUrl);
req.Headers.Add("x-ms-date", string.Format("{0:R}", reqDate));
req.Headers.Add("authorization", authHeader);
req.ContentType = contentType;
req.Method = method;
WebResponse resp = req.GetResponse();
using (StreamReader sr = new StreamReader(resp.GetResponseStream(), true))
{
rtn = sr.ReadToEnd();
sr.Close();
}
resp.Close();
Console.WriteLine(rtn);
}
catch (WebException wex)
{
HttpWebResponse wrsp = (HttpWebResponse)wex.Response;
Console.WriteLine(string.Format("{0} : {1}", wrsp.StatusCode,wrsp.StatusDescription));
}
catch (HttpException hex)
{
Console.WriteLine(hex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
// config reading helpers
private static string GetConfigItem(string key)
{
return GetConfigItem(key, string.Empty);
}
private static string GetConfigItem(string key, string defaultValue)
{
return (ConfigurationSettings.AppSettings[key] != null ? ConfigurationSettings.AppSettings[key] : defaultValue);
}
// hashing helper
private static string ComputeMacSha(string canonicalizedString, byte[] key)
{
byte[] dataToMAC = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
using (HMACSHA256 hmacsha1 = new HMACSHA256(key))
{
return System.Convert.ToBase64String(hmacsha1.ComputeHash(dataToMAC));
}
}
}
}