I'm investigating an error that has only happened with the Put Append operation in my application which communicates with Azure Blob Service thanks to the Rest API.
The creation of the file has succeeded but since it's an append blob, I had to add content with Append Block operation and I'm getting a 403 Forbidden WebException when I try to do so :
<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:00dc0116-0001-00c2-066b-cbafe8000000
Time:2017-05-12T22:01:16.1689598Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request 'N7UVKFwftf2YnAFdnciRneu7LsAkWHKXUpwhFRxlQqI=' is not the same as any computed signature. Server used following string to sign: 'PUT
800
x-ms-date:Fri, 12 May 2017 22:01:15 GMT
x-ms-version:2016-05-31
/<myaccount>/write/FixedRecord10r.txt
comp:appendblock'.</AuthenticationErrorDetail></Error>
However, in the method where I construct the signature I displayed the stringToSign and it's exactly the same as the server used !
PUT
800
x-ms-date:Fri, 12 May 2017 22:01:15 GMT
x-ms-version:2016-05-31
/<myaccount>/write/FixedRecord10r.txt
comp:appendblock
Here is the header I parametrized :
public void AppendFile(MemoryStream stream)
{
string dateFormatted = string.Format(CultureInfo.InvariantCulture, "{0:R}", DateTime.UtcNow);
string signature = GetSignature("PUT", "xxxxxxx", afsAccount, dateFormatted, null, null, stream.Length, null);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://" + afsAccount + ".blob.core.windows.net" + path + "?comp=appendblock");
request.Headers.Add("x-ms-version", "2016-05-31");
request.Headers.Add("x-ms-date", dateFormatted);
request.Headers.Add(HttpRequestHeader.Authorization, "SharedKey " + afsAccount + ":" + signature);
request.Method = "PUT";
request.ContentLength = stream.Length;
using (var requestStream = request.GetRequestStream())
{
stream.Position = 0;
stream.CopyTo(requestStream);
}
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()){
...
}
private String GetSignature(string verb, string azureAccessKey, string afsAccount, string date, string byteRange, string containerName, long contentLength, string blobType)
{
if(byteRange != null)
{
byteRange = "x-ms-range:" + byteRange + "\n";
}
string path = "/" + afsAccount + Dfs.Path;
if (containerName != null)
{
path = "/" + afsAccount + "/" + containerName + "\nrestype:container";
}else if (contentLength > 0)
{
path += " \ncomp:appendblock";
}
string length = "\n";
if (contentLength != 0)
{
length = contentLength.ToString() + "\n";
}
if (blobType != null)
{
blobType = "x-ms-blob-type:" + blobType + "\n";
}
// construct input value
string inputValue = verb + "\n" +
"\n" + /*Content-Encoding*/
"\n" + /*Content-Language*/
length + /*Content-Length*/
"\n" + /*Content-MD5*/
"\n" + /*Content-Type*/
"\n" + /*Date*/
"\n" + /*If-Modified-Since*/
"\n" + /*If-Match*/
"\n" + /*If-None-Match*/
"\n" + /*If-Unmodified-Since*/
"\n" + /*Range*/
blobType +
"x-ms-date:" + date + "\n" +
byteRange +
"x-ms-version:2016-05-31\n" +
path;
Console.WriteLine(inputValue);
// create base64 encoded signature
var hmac = new HMACSHA256();
hmac.Key = Convert.FromBase64String(azureAccessKey);
byte[] sigbyte = hmac.ComputeHash(Encoding.UTF8.GetBytes(inputValue));
var signature = Convert.ToBase64String(sigbyte);
return signature;
}
I'm now wondering if I missed something or if there is a problem with this specific operation as I've never had the problem with other operations.
{ path += " \ncomp:appendblock"; }(between"and\n). Can you remove that and try your request again? - Gaurav Mantri