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