简体   繁体   中英

Azure v2 GetResponse fails to authenticate. Error 403: Forbidden

I'm migrating (sigh) Windows Azure from 1.7 to version 2, and I'm now facing some troubles with the old authentication way. When I try to execute the next code (working with the old implementations of the Azure SDK)...

[...]

var policy = new SharedAccessBlobPolicy();
policy.SharedAccessStartTime = new DateTimeOffset(DateTime.UtcNow.AddMinutes(-5));
policy.SharedAccessExpiryTime = policy.SharedAccessStartTime.Value.AddMinutes(5);
policy.Permissions = SharedAccessBlobPermissions.Read;

var sas = blobContainer.GetSharedAccessSignature(policy)
var request = WebRequest.Create(string.Format("{0}/{1}{2}", containerUri, blobName, sas));
request.Method = "GET";
var headers = new NameValueCollection();
headers.Add("x-ms-blob-type", "BlockBlob");
request.Headers.Add(headers);
request.ContentLength = 0;

var response = (HttpWebResponse)request.GetResponse();

[...]

... the GetResponse() method gets mad at me, and throws a WebException saying " Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. "

The ResponseUri is http://127.0.0.1:10000/devstoreaccount1/testcontainer/testBlob?sv=2012-02-12&st=2013-01-22T09.52.27Z&se=2013-01-22T09.57.29Z&sr=c&sp=r&sig=WxFfIg9NxKodH7zGjKRym7RuXd61F5jlG6ILtG1UYPg%3D , and it looks fine to me. I thought it was a problem of the AccountKey, but I have the same issue when trying it on a real storage, with the correct key provided by the Azure portal.

Is there any property or new initializations to be done for the new REST API?

UPDATE : I've tried the console application developed by @Gaurav Mantri in his reply, but it still didn't work for me. So, I suspect the problem could depends on the italian localization on my machine, or some things related to Windows 8 (on the machine of another colleague the console application didn't work aswell, with the same Error 403, forbidden! thrown by the GetResponse ). I noticed the URI we get differs from every single example I found on the net, so I see the starting and expiry times as (for example) 2013-01-22T09.52.27Z instead of 2013-01-22T09%3A52%3A27Z

Ok, I've found which was the problem.

Adding the line Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; to my code (or test initiliazer) will resolve the problem.

Looking at the WindowsAzure.Storage sources, when the service creates the signature, it uses the format string "yyyy-MM-ddTHH:mm:ssZ", without passing CultureInfo.InvariantCulture as parameter to the ToString method. But for italian regional settings, the separator between hours, minutes and seconds are dots, where for US are two-point. So my local machine and the remote machine created two different signatures, and they weren't able to match.

I'm going to report this as bug (or issue), cause not everyone has the same regional settings.

For GET operation you don't need to add "x-ms-blob-type" request header. It is only required when you're uploading a blob. Please try again after removing following lines of code:

var headers = new NameValueCollection();
headers.Add("x-ms-blob-type", "BlockBlob");
request.Headers.Add(headers);

Update: Here's the complete code for reading a blob using SAS URI:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Net;
using System.IO;
namespace ConsoleApplication26
{
    class Program
    {
        static void Main(string[] args)
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
            CloudBlobContainer blobContainer = storageAccount.CreateCloudBlobClient().GetContainerReference("temp");
            //Create blob container.
            blobContainer.CreateIfNotExists();
            string blobContents = "Let's test SAS out :)";
            byte[] bytes = Encoding.UTF8.GetBytes(blobContents);

            string blobName = "sastest.txt";
            CloudBlockBlob blob = blobContainer.GetBlockBlobReference(blobName);
            //Upload blob
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                blob.UploadFromStream(ms);
            }
            //Get SAS Uri
            var sasUri = GetSasUrl(blobContainer, blobName);
            DownloadBlob(sasUri);

            Console.ReadLine();

        }

        static Uri GetSasUrl(CloudBlobContainer blobContainer, string blobName)
        {
            var policy = new SharedAccessBlobPolicy()
            {
                SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),
                SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),
                Permissions = SharedAccessBlobPermissions.Read,
            };

            var sas = blobContainer.GetSharedAccessSignature(policy);
            return new Uri(string.Format("{0}/{1}{2}", blobContainer.Uri, blobName, sas));
        }

        static void DownloadBlob(Uri sasUri)
        {
            var request = (HttpWebRequest)WebRequest.Create(sasUri);
            request.Method = "GET";
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                long responseContentLength = response.ContentLength;
                byte[] fetchedContents = new byte[(int) responseContentLength];
                using (var stream = response.GetResponseStream())
                {
                    stream.Read(fetchedContents, 0, fetchedContents.Length);
                }
                string blobContents = Encoding.UTF8.GetString(fetchedContents);
                Console.WriteLine("Blob Contents: " + blobContents);
            }
        }
    }
}

I got this same error when trying to upload a file. I did a bit of searching and none of the suggested solutions were working.

Then I checked the http response object in the inner exception and discovered that I had accidentally created the blob using not a filename but the full file path (c:\\folder\\folder\\file.jpg instead of just file.jpb), so the url it was trying to upload to did not exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM