Azure REST API创建容器,.NET客户端配置文件,禁止使用403,找到的MAC签名与任何计算出的签名都不相同 [英] Azure REST API Create Container, .NET Client Profile, 403 Forbidden, The MAC signature found is not the same as any computed signature
问题描述
在阅读完所有与此相关的帖子后,我仍然找不到解决方案,并且在摆弄"了几个小时之后,在这里发表了一篇帖子,希望有人对它有所了解:
after reading all posts that come close to this, I still find no solution and after "fiddling" around for hours, here goes a post, in hopes that someone has an idea:
我找到的最接近的答复是此处
The closest response I have found is here
,但这些帖子中有两件事不适用:1.我正在使用在大多数情况下肯定有效的示例代码.2.由于我必须使用.NET客户端配置文件,因此我无权访问任何Azure StorageClient简化程序.3.这可能是一个愚蠢的错误,我只是没看到:-)
but two things from these posts do not apply: 1. I'm using example code that certainly works in most cases. 2. I do not have access to any Azure StorageClient simplifiers, since I am required to use the .NET client profile. 3. It is probably a stupid error that I just don't see :-)
这是电话:
// Create Blob
// using REST: http://msdn.microsoft.com/en-us/library/windowsazure/dd135733.aspx
BlobHelper blobHelper = new BlobHelper(_storageAccountName, _storageKey);
Console.WriteLine("Create container...");
if (!blobHelper.CreateContainer("mytestcontainer")) //(blobInfo.ContainerName))
{
Console.WriteLine("REST: Create Blob Failed");
}
这是电汇请求(提琴手):
And here is wire request (fiddler):
PUT /devstoreaccount1/mytestcontainer?restype=container HTTP/1.1
x-ms-date: Mon, 25 Jun 2012 16:54:01 GMT
x-ms-version: 2009-09-19
Authorization: SharedKey devstoreaccount1:REpHdtTSQrwGtXuEbLJmRQdpe/j2l5icmGUeFkQ09jw=
Host: 127.0.0.1:10000
Content-Length: 0
Connection: Keep-Alive
无论我使用实时Azure存储还是开发人员存储,都会发生相同的事情.
The same thing happens whether I use live Azure Storage or the Developer Storage.
这是电线的响应:
HTTP/1.1 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Content-Length: 698
Content-Type: application/xml
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: d5430ddc-f146-4102-b8db-a8bfab0ed82f
Date: Mon, 25 Jun 2012 16:54:00 GMT
<?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:d5430ddc-f146-4102-b8db-a8bfab0ed82f
Time:2012-06-25T16:54:01.3354093Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request 'REpHdtTSQrwGtXuEbLJmRQdpe/j2l5icmGUeFkQ09jw=' is not the same as any computed signature. Server used following string to sign: 'PUT
0
x-ms-date:Mon, 25 Jun 2012 16:54:01 GMT
x-ms-version:2009-09-19
/devstoreaccount1/devstoreaccount1/mytestcontainer
restype:container'.</AuthenticationErrorDetail></Error>
不用说,这让我发疯,所以任何提示都值得赞赏.
Needless to say, this driving me nuts, so any tips appreciated.
直接来自Azure存储 REST API示例的源代码,RESTHelper.cs.这在许多情况下都适用,但不适用于以下情况:
The source code, which is directly from the Azure Storage REST API sample, RESTHelper.cs. This works in many scenarios, but not this one:
#region REST HTTP Request Helper Methods
// Construct and issue a REST request and return the response.
public HttpWebRequest CreateRESTRequest(string method, string resource, string requestBody = null, SortedList<string, string> headers = null,
string ifMatch = "", string md5 = "")
{
byte[] byteArray = null;
DateTime now = DateTime.UtcNow;
string uri = Endpoint + resource;
HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;
request.Method = method;
request.ContentLength = 0;
request.Headers.Add("x-ms-date", now.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
request.Headers.Add("x-ms-version", "2009-09-19"); //2009-09-19, 2011-08-18
if (IsTableStorage)
{
request.ContentType = "application/atom+xml";
request.Headers.Add("DataServiceVersion", "1.0;NetFx");
request.Headers.Add("MaxDataServiceVersion", "1.0;NetFx");
}
if (headers != null)
{
foreach (KeyValuePair<string, string> header in headers)
{
request.Headers.Add(header.Key, header.Value);
}
}
if (!String.IsNullOrEmpty(requestBody))
{
request.Headers.Add("Accept-Charset", "UTF-8");
byteArray = Encoding.UTF8.GetBytes(requestBody);
request.ContentLength = byteArray.Length;
}
request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, ifMatch, md5));
if (!String.IsNullOrEmpty(requestBody))
{
request.GetRequestStream().Write(byteArray, 0, byteArray.Length);
}
return request;
}
// Generate an authorization header.
//RHT: http://msdn.microsoft.com/en-us/library/dd179428.aspx
public string AuthorizationHeader(string method, DateTime now, HttpWebRequest request, string ifMatch = "", string md5 = "")
{
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
);
}
byte[] SignatureBytes = System.Text.Encoding.UTF8.GetBytes(MessageSignature);
System.Security.Cryptography.HMACSHA256 SHA256 = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(StorageKey));
String AuthorizationHeader = "SharedKey " + StorageAccount + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));
return AuthorizationHeader;
}
// Get canonicalized headers.
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();
}
// Get header values.
public ArrayList GetHeaderValues(NameValueCollection headers, string headerName)
{
ArrayList list = new ArrayList();
string[] values = headers.GetValues(headerName);
if (values != null)
{
foreach (string str in values)
{
list.Add(str.TrimStart(null));
}
}
return list;
}
// Get canonicalized resource.
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)
{
//Uri.EscapeDataString(...)
//WebUtility.HtmlEncode(...)
//https://stackoverflow.com/questions/36315/alternative-to-httputility-for-net-3-5-sp1-client-framework
//NameValueCollection values = HttpUtility.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();
}
#endregion
推荐答案
感谢Smarx,
的确是大开眼界,它使我想到了解决方案:
Indeed, an eye opener, and it led me to the solution:
.NET客户端配置文件缺少HttpUtility,因此我用不提供构建器所需格式的正则表达式替换了它.所以,我用这个替换了它:
The .NET Client Profile is missing HttpUtility, so I replaced it with a regular expression that was not providing the format required by the builder. So, I replaced it with this one:
http://google-gdata.googlecode.com/svn/trunk/clients/cs/src/core/HttpUtility.cs
,并且有效.标头现在看起来像这样:
and it works. Here is what the header now looks like:
PUT /devstoreaccount1/mytestcontainer?restype=container HTTP/1.1
x-ms-date: Mon, 25 Jun 2012 18:57:04 GMT
x-ms-version: 2009-09-19
Authorization: SharedKey devstoreaccount1:J5D1E7PK/yNBgQITHmYgVuu4cHtcGad+YKGb1Lh/YUU=
Host: 127.0.0.1:10000
Content-Length: 0
结果:
HTTP/1.1 201 Created
Transfer-Encoding: chunked
Last-Modified: Mon, 25 Jun 2012 18:57:04 GMT
ETag: 0x8CF211B72040930
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
x-ms-request-id: 079bcdc1-a7fa-4207-99cd-b7f1c2d1b981
x-ms-version: 2009-09-19
Date: Mon, 25 Jun 2012 18:57:04 GMT
0
我已经遍历了代码,但是没有看到这种区别,再次感谢,R.
I had stepped through the code, but wasn't seeing this difference, thanks again, R.
这篇关于Azure REST API创建容器,.NET客户端配置文件,禁止使用403,找到的MAC签名与任何计算出的签名都不相同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!