locked
BLOB REST authentication examples RRS feed

  • Question

  • Searched net for examples and have basic framework, but still getting 403 errors.
    Does anyone have a working BLOB REST authentication example?

    Thanks in advance.
    Monday, January 25, 2010 5:47 PM

Answers

  • 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)

    Monday, January 25, 2010 8:44 PM
    Answerer

All replies

  • Hi,

    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:21 PM
  • Thanks Gaurav, definitely, be back asap to post what I do have...
    Monday, January 25, 2010 6:30 PM
  • 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.
    Monday, January 25, 2010 6:40 PM
    Answerer
  • Thank 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:13 PM
  • 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)

    Monday, January 25, 2010 8:44 PM
    Answerer
  • Will do, and will post notes here. Thanks again Neil.
    Monday, January 25, 2010 8:51 PM
  • 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 12:53 AM
  • 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 3:02 AM
    Answerer
  • Thanks very much.
    Monday, April 19, 2010 9:58 AM
  • 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 4:55 PM
  • 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);
    
    
    Monday, September 27, 2010 6:25 PM
    Answerer
  • Thanks 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
    Tuesday, September 28, 2010 7:59 AM