2012-06-25 6 views
5

después de leer todas las publicaciones que se acercan a esto, todavía no encuentro solución y después de "tocar el violín" alrededor durante horas, aquí va un post, con la esperanza de que alguien tiene una idea:Azure REST API Create Container, .NET Client Profile, 403 Forbidden, La firma MAC encontrada no es la misma que cualquier firma calculada

La respuesta más cercano que he encontrado es here

pero hay dos cosas de estos mensajes no se aplican: 1. estoy usando código de ejemplo que sin duda funciona en la mayoría de los casos. 2. No tengo acceso a ningún simplificador de Azure StorageClient, ya que estoy obligado a utilizar el perfil de cliente .NET. 3. Es probablemente un error estúpido que no veo :-)

Aquí es la llamada:

// 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"); 
    } 

Y aquí es petición de alambre (violinista):

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 

Lo mismo sucede si utilizo Live Azure Storage o Developer Storage.

Aquí está la respuesta de alambre:

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> 

hace falta decir que esto me está volviendo loco, por lo que cualquier consejos apreciados.

El código fuente, que es directamente de Azure Storage REST API sample, RESTHelper.cs. Esto funciona en muchos escenarios, pero no ésta:

#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 
+2

Intente poner 'Console.WriteLine (MessageSignature)' cerca del final de 'AuthorizationHeader' y compárelo con la cadena que el servicio de almacenamiento dice que está usando. La diferencia debería ser reveladora. – smarx

Respuesta

3

Gracias Smarx,

De hecho, un abridor de ojos, y me llevó a la solución:

El perfil de cliente .NET no se encuentra HttpUtility, así que lo reemplacé con una expresión regular que no proporcionaba el formato requerido por el generador. Por lo tanto, lo sustituyó con éste:

http://google-gdata.googlecode.com/svn/trunk/clients/cs/src/core/HttpUtility.cs

y funciona. Esto es lo que la cabecera ahora queda como:

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 

Y resultado:

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 

que había entrado a través del código, pero no estaba viendo esta diferencia, gracias de nuevo, R.

+0

#GGleGrand ¿Puedo saber en qué entorno ha ejecutado lo anterior? Estoy tratando de hacer lo mismo a través de la API REST. Si es posible, ¿puedes echarle un vistazo al hilo a continuación y compartir algo de sombra sobre él: https://stackoverflow.com/questions/45976574/azure-rest-api-communication – Rv1

+0

Sí, en el momento en que fue win7, VisualStudio2013 , .NET4 Client Profile, Azure Storage – GGleGrand

Cuestiones relacionadas