BLOB REST authentication examples
-
Monday, January 25, 2010 5:47 PMSearched net for examples and have basic framework, but still getting 403 errors.Does anyone have a working BLOB REST authentication example?Thanks in advance.
- Moved by Brian AurichMicrosoft Employee, Moderator Tuesday, September 28, 2010 7:01 PM migration (From:Windows Azure)
All Replies
-
Monday, January 25, 2010 6:21 PMHi,
Will you be able to post some code so that we can take a look?
Thanks
Gaurav Mantri
Cerebrata Software
http://www.cerebrata.com -
Monday, January 25, 2010 6:30 PMThanks Gaurav, definitely, be back asap to post what I do have...
-
Monday, January 25, 2010 6:40 PMAnswerer
Here is an example of using the REST API to retrieve a list of containers:
private void GetBlobs() { string requestMethod = "GET"; String urlPath = "comp=list"; String urlPathResource = "comp:list"; String msVersion = "2009-09-19"; String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture); String canonicalizedHeaders = String.Format("x-ms-date:{0}\nx-ms-version:{1}", dateInRfc1123Format, msVersion); String canonicalizedResource = String.Format("/{0}/\n{1}", AzureStorageConstants.Account, urlPathResource); String stringToSign = String.Format("{0}\n\n\n\n\n\n\n\n\n\n\n\n{1}\n{2}", requestMethod, canonicalizedHeaders, canonicalizedResource); String authorizationHeader = CreateAuthorizationHeader(stringToSign); Uri uri = new Uri(AzureStorageConstants.BlobEndPoint + "?" + urlPath); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.Method = requestMethod; request.Headers.Add("x-ms-date", dateInRfc1123Format); request.Headers.Add("x-ms-version", msVersion); request.Headers.Add("Authorization", authorizationHeader); request.Headers.Add("Accept-Charset", "UTF-8"); request.Accept = "application/atom+xml,application/xml"; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { Stream dataStream = response.GetResponseStream(); using (StreamReader reader = new StreamReader(dataStream)) { String responseFromServer = reader.ReadToEnd(); } } } private String CreateAuthorizationHeader(String canonicalizedString) { if (String.IsNullOrEmpty(canonicalizedString)) { throw new ArgumentNullException("canonicalizedString"); } String signature = CreateHmacSignature(canonicalizedString, AzureStorageConstants.Key); String authorizationHeader = String.Format(CultureInfo.InvariantCulture, "{0} {1}:{2}", AzureStorageConstants.SharedKeyAuthorizationScheme, AzureStorageConstants.Account, signature); return authorizationHeader; } private String CreateHmacSignature(String unsignedString, Byte[] key) { if (String.IsNullOrEmpty(unsignedString)) { throw new ArgumentNullException("unsignedString"); } if (key == null) { throw new ArgumentNullException("key"); } Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(unsignedString); using (HMACSHA256 hmacSha256 = new HMACSHA256(key)) { return Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac)); } }
Note the unfortunate usage where urlPath uses comp=list while urlPathResource uses comp:list. AzureStorageConstants is a trivial class exposing the Azure account details and endpoints.
I just tried this out so I know it works.- Edited by Neil MackenzieMVP, Editor Wednesday, July 07, 2010 11:34 PM Corrected this to list of containers not blobs
-
Monday, January 25, 2010 8:13 PMThank you Neil,I used your code with my account/endpoint info but still get 403.Can you tell if the values I'm using "look" right please? I'm wondering if its simply the account value?var BlobEndPoint = "http://expressionsoft.blob.core.windows.net/";var Account = "expressionsoft";var SharedKeyAuthorizationScheme = "SharedKey";byte[] Key = Encoding.UTF8.GetBytes("Base 64 Secondary Access Key");One thing I should mentions is that I expect the GET to return a null or empty list since I haven't submitted any PUT requests yet.
-
Monday, January 25, 2010 8:44 PMAnswerer
jmarquez -
If you haven't created a container and some blobs I suggest you download Gaurav Mantri's Cloud Storage Studio and use that to create a container and upload some files. Doing this has the advantage that using a third-party tool to access Azure Storage Services demonstrates that there are no problems accessing Azure Storage from your machine - and that you are using the right account and key. You should also use Fiddler to verify precisely what you are sending across the wire. The response visible in Fiddler typically provides additional detail as what the Azure Storage Service discovered to be wrong with your request.
You should verify that the data is in the correct RFC1123 format - and that it is correct. Azure gives you a 15 minute leeway on the time but if your system is too far off authentication will fail. You should verify that your account key is correct - a good clue that you copied it correctly is that base 64 keys often end with either one or two = symbols.
I did forget to provide one piece of code and that was used to convert the Base 64 key into an array of Bytes. AzureStorageConstants.Key uses the following to return the key as a Byte array
Convert.FromBase64String(cloudKey)
- Edited by Neil MackenzieMVP, Editor Monday, January 25, 2010 8:59 PM
- Marked As Answer by Yi-Lun LuoModerator Monday, February 01, 2010 9:36 AM
-
Monday, January 25, 2010 8:51 PMWill do, and will post notes here. Thanks again Neil.
-
Monday, April 19, 2010 12:53 AM
Hi,
Excellent post, really helped me get my head around the string signing. Using your code as a template I was trying to do PUTs but am gettting the "Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature." I'm not sure what i'm doing wrong here, any help would be much appreciated.
string requestMethod = "PUT"; String msVersion = "2009-09-19"; String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture); String canonicalizedHeaders = String.Format("x-ms-blob-type:{2}\nx-ms-date:{0}\nx-ms-version:{1}", dateInRfc1123Format, msVersion, "BlockBlob"); String canonicalizedResource = String.Format("/{0}/{1}/{2}", account, container, xmlBlobNew); String stringToSign = String.Format("{0}\n\n\n{4}\n\n{3}\n\n\n\n\n\n\n{1}\n{2}", requestMethod, canonicalizedHeaders, canonicalizedResource, "Content-Type:text/plain", "Content-Length:11"); String authorizationHeader = CreateAuthorizationHeader(stringToSign); Uri uri = new Uri(blobEndPoint + container + @"/" + xmlBlobNew ); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.Method = requestMethod; request.Headers.Add("x-ms-blob-type", "BlockBlob"); request.Headers.Add("x-ms-date", dateInRfc1123Format); request.Headers.Add("x-ms-version", msVersion); request.Headers.Add("Authorization", authorizationHeader); request.Headers.Add("Accept-Charset", "UTF-8"); request.ContentType = "text/plain"; request.ContentLength = 11; request.Accept = "application/atom+xml,application/xml, text/plain"; ...
-
Monday, April 19, 2010 3:02 AMAnswerer
JimmieBond -
The following works:
private void PutBlob(String containerName, String blobName) { String requestMethod = "PUT"; String content = "The Name of This Band is Talking Heads"; UTF8Encoding utf8Encoding = new UTF8Encoding(); Byte[] blobContent = utf8Encoding.GetBytes(content); Int32 blobLength = blobContent.Length; const String blobType = "BlockBlob"; String urlPath = String.Format("{0}/{1}", containerName, blobName); String msVersion = "2009-09-19"; String dateInRfc1123Format = DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture); String canonicalizedHeaders = String.Format("x-ms-blob-type:{0}\nx-ms-date:{1}\nx-ms-version:{2}", blobType, dateInRfc1123Format, msVersion); String canonicalizedResource = String.Format("/{0}/{1}", AzureStorageConstants.Account, urlPath); String stringToSign = String.Format("{0}\n\n\n{1}\n\n\n\n\n\n\n\n\n{2}\n{3}", requestMethod, blobLength, canonicalizedHeaders, canonicalizedResource); String authorizationHeader = CreateAuthorizationHeader(stringToSign); Uri uri = new Uri(AzureStorageConstants.BlobEndPoint + urlPath); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.Method = requestMethod; request.Headers.Add("x-ms-blob-type", blobType); request.Headers.Add("x-ms-date", dateInRfc1123Format); request.Headers.Add("x-ms-version", msVersion); request.Headers.Add("Authorization", authorizationHeader); request.ContentLength = blobLength; using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(blobContent, 0, blobLength); } using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { String ETag = response.Headers["ETag"]; } }- Proposed As Answer by Jimmiebond Monday, April 19, 2010 9:58 AM
-
Monday, April 19, 2010 9:58 AMThanks very much.
-
Monday, September 27, 2010 4:55 PM
Hi, I am trying your code for write in my blob, but method GetResponse() gives me the error 'Forbbiden (403)'. I think that the cause is a bad Authorization but I can't find how to fix that issue.
I'm using local storage:
String AzureStorageConstantsBlobEndPoint = "http://127.0.0.1:10000/"; String AzureStorageConstantsAccount = "devstoreaccount1";
private static String CreateAuthorizationHeader(String canonicalizedString) { String AzureStorageConstantsKey = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="; String AzureStorageConstantsAccount = "devstoreaccount1"; String AzureStorageConstantsSharedKeyAuthorizationScheme = "SharedKey"; String signature = string.Empty; using (HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(AzureStorageConstantsKey))) { Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString); signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac)); } String authorizationHeader = String.Format( CultureInfo.InvariantCulture, "{0} {1}:{2}", AzureStorageConstantsSharedKeyAuthorizationScheme, AzureStorageConstantsAccount, signature); return authorizationHeader; }
I just changed this line of code:
Uri uri = new Uri(AzureStorageConstantsBlobEndPoint + AzureStorageConstantsAccount + "/" + urlPath);
because if I don't put account in the uri, I get a 404 error.
Did I miss anything?? I'm a little stuck...
Thanks in advance!!
-
Monday, September 27, 2010 6:25 PMAnswerer
Carlimir -
The issue is that the authorization string is slightly different in development storage - and requires the account to be specified twice. When trying out things with the REST interface I prefer to go directly against cloud storage because of these differences and because the Fiddler experience is better. (You should be using Fiddler.)
In the REST examples I post I tend to focus on code against cloud storage to simplify the code (i.e. I don't give the alternatives.) In this particular case, the solution is to change the declaration of canonicalizedResource to the following (for development storage):
String canonicalizedResource = String.Format("/{0}/{0}/{1}", AzureStorageConstants.Account, urlPath); -
Tuesday, September 28, 2010 7:59 AMThanks for your response Neil, I solved my issue completely.I just download Fiddler and I find it very interesting. I'll take a look when I can.
Regards

