C# Azure REST API : The MAC signature […] is not t

2019-08-28 22:15发布

问题:

I'm working on a project that uses Azure blob storage.

My code send the following in order to upload a jpeg image to an Azure storage blob:

PUT https://<storageAccount>.blob.core.windows.net/<container>/ca13bd3a-d805-46ce-994e-e70560a54cda.jpg HTTP/1.1
x-ms-date: Tue, 08 Aug 2017 10:05:24 GMT
Date: Tue, 08 Aug 2017 10:05:24 GMT
x-ms-version: 2009-09-19
Content-Type: image/jpeg
x-ms-blob-type: BlockBlob
Accept-Charset: UTF-8
Authorization: SharedKey <storageAccount>:<computed hash>
Host: <storageAccount>.blob.core.windows.net
Content-Length: 498732
Expect: 100-continue
Connection: Keep-Alive

<jpeg image>

The Autorization Header is created with the following code :

    byte[] SignatureBytes = System.Text.Encoding.UTF8.GetBytes(MessageSignature);
    System.Security.Cryptography.HMACSHA256 SHA256 = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(StorageKey));
    String AuthorizationHeaderRes = "SharedKey " + StorageAccount + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));

But as a result, I get a 403 error with the following message :

The MAC signature found in the HTTP request '<request id>' is not the same as any computed signature. Server used following string to sign: 'PUT


498732

image/jpeg






x-ms-blob-type:BlockBlob
x-ms-date:Tue, 08 Aug 2017 10:05:24 GMT
x-ms-version:2009-09-19
/<storageAccount>/<storageAccount>/ca13bd3a-d805-46ce-994e-e70560a54cda.jpg

I tried a lot of solution I've fount on the web, thus I know the following :

  • DateTime is OK
  • I can get the list of the blobs in the container

EDIT 1 :

MessageSignature :

  string MessageSignature;

        if (IsTableStorage)
        {
            MessageSignature = String.Format("{0}\n\n{1}\n{2}\n{3}",
                method,
                "application/atom+xml",
                now.ToString("R", System.Globalization.CultureInfo.InvariantCulture),
                GetCanonicalizedResource(request.RequestUri, StorageAccount)
                );
        }
        else
        {
            MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}",
                method,
                (method == "GET" || method == "HEAD") ? String.Empty : request.ContentLength.ToString(),
                ifMatch,
                GetCanonicalizedHeaders(request),
                GetCanonicalizedResource(request.RequestUri, StorageAccount),
                md5
                );
        }

EDIT 2 :

GetCanonicalizedHeaders :

        public string GetCanonicalizedHeaders(HttpWebRequest request)
    {
        ArrayList headerNameList = new ArrayList();
        StringBuilder sb = new StringBuilder();
        foreach (string headerName in request.Headers.Keys)
        {
            if (headerName.ToLowerInvariant().StartsWith("x-ms-", StringComparison.Ordinal))
            {
                headerNameList.Add(headerName.ToLowerInvariant());
            }
        }
        headerNameList.Sort();
        foreach (string headerName in headerNameList)
        {
            StringBuilder builder = new StringBuilder(headerName);
            string separator = ":";
            foreach (string headerValue in GetHeaderValues(request.Headers, headerName))
            {
                string trimmedValue = headerValue.Replace("\r\n", String.Empty);
                builder.Append(separator);
                builder.Append(trimmedValue);
                separator = ",";
            }
            sb.Append(builder.ToString());
            sb.Append("\n");
        }
        return sb.ToString();
    }

GetCanonicalizedResource :

public string GetCanonicalizedResource(Uri address, string accountName)
    {
        StringBuilder str = new StringBuilder();
        StringBuilder builder = new StringBuilder("/");
        builder.Append(accountName);
        builder.Append(address.AbsolutePath);
        str.Append(builder.ToString());
        NameValueCollection values2 = new NameValueCollection();
        if (!IsTableStorage)
        {
            //NameValueCollection values = System.Web.HttpUtility.ParseQueryString(address.Query);
            NameValueCollection values = this.ParseQueryString(address.Query);
            foreach (string str2 in values.Keys)
            {
                ArrayList list = new ArrayList(values.GetValues(str2));
                list.Sort();
                StringBuilder builder2 = new StringBuilder();
                foreach (object obj2 in list)
                {
                    if (builder2.Length > 0)
                    {
                        builder2.Append(",");
                    }
                    builder2.Append(obj2.ToString());
                }
                values2.Add((str2 == null) ? str2 : str2.ToLowerInvariant(), builder2.ToString());
            }
        }
        ArrayList list2 = new ArrayList(values2.AllKeys);
        list2.Sort();
        foreach (string str3 in list2)
        {
            StringBuilder builder3 = new StringBuilder(string.Empty);
            builder3.Append(str3);
            builder3.Append(":");
            builder3.Append(values2[str3]);
            str.Append("\n");
            str.Append(builder3.ToString());
        }
        return str.ToString();
    }

EDIT 3 :

MessageSignature content (note : date changed, but it is irrelevant):

PUT


498732








x-ms-blob-type:BlockBlob
x-ms-date:Tue, 08 Aug 2017 15:09:52 GMT
x-ms-version:2009-09-19
/<storageAccount>/<container>/ca13bd3a-d805-46ce-994e-e70560a54cda.jpg
标签: c# rest azure