2009-07-23 7 views
6

Ho riscontrato un problema con la codifica dell'hash per la firma della versione 2 dell'ec2 API.Codifica della firma della versione 2 di Amazon ec2 API con C#

Nota mia versione 1 Firma hashing funziona bene, ma questo sono ammortizzati e che ho bisogno di passare alla versione 2. Quindi, in primo luogo qui è il codice che funziona ...

parametri è solo un dizionario, quello che ho è sufficiente ordinare i parametri per chiave e aggiungere ogni coppia di valori senza delimitatori, quindi eseguire l'hash della stringa con la mia chiave. (Ancora una volta, notare che questo funziona bene)

private string GetVersion1Sig() 
{ 
    string sig = string.Join(string.Empty, parameters.OrderBy(vp => vp.Key).Select(p => string.Format("{0}{1}", p.Key, p.Value)).ToArray()); 
    UTF8Encoding encoding = new UTF8Encoding(); 
    HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey)); 
    byte[] hash = signature.ComputeHash(encoding.GetBytes(sig)); 
    string result = Convert.ToBase64String(hash); 
    return result; 
} 

Ora, con la versione 2 ci sono alcuni cambiamenti, qui è la doco dalla guida sviluppatori API ...

  1. Creare la stringa di query che si Canonicalized necessario più avanti in questa procedura:

a. Ordinare i componenti della stringa di query UTF-8 in base al nome del parametro con l'ordinamento di byte naturale. I parametri possono provenire dall'URI GET o dal corpo POST (quando Content-Type è application/x-www-form-urlencoded).

b. L'URL codifica il nome e i valori dei parametri in base alle seguenti regole:

• Non codificare URL per nessuno dei caratteri non prenotati definiti da RFC 3986. Questi caratteri non riservati sono A-Z, a-z, 0-9, trattino (-), trattino basso (_), punto (.), e tilde (~).
• La percentuale codifica tutti gli altri caratteri con% XY, dove X e Y sono caratteri esadecimali 0-9 e maiuscolo A-F.
• La percentuale codifica caratteri UTF-8 estesi nella forma% XY% ZA ....
• La percentuale codifica il carattere dello spazio come% 20 (e non +, come fanno gli schemi di codifica comuni ).

Nota
Attualmente tutti i nomi dei parametri del servizio AWS utilizzano caratteri non prenotati, pertanto non è necessario codificarli per . Tuttavia, è possibile includere il codice per gestire i nomi dei parametri che utilizzano caratteri riservati, per un possibile utilizzo futuro.

c. Separare i nomi dei parametri codificati dai loro valori codificati con il segno di uguale (=) (carattere ASCII 61), anche se il valore del parametro è vuoto.

d. Separare le coppie nome-valore con una e commerciale (&) (codice ASCII 38).

  1. Creare la stringa da firmare in base alla seguente pseudo-grammatica ("\ n" rappresenta una riga nuova ASCII ). StringToSign = HTTPVerb + "\ n" + ValueOfHostHeaderInLowercase + "\ n" + HTTPRequestURI + "\ n" +
    CanonicalizedQueryString Il componente HTTPRequestURI è il componente percorso assoluto HTTP del URI fino a, ma non tra cui, la stringa di query. Se HTTPRequestURI è vuoto, utilizzare una barra (/).
  2. Calcolare un HMAC conforme a RFC 2104 con la stringa appena creata, la chiave di accesso segreto come chiave e SHA256 o SHA1 come algoritmo di hash. Per ulteriori informazioni, andare a http://www.rfc.net/rfc2104.html.
  3. Convertire il valore risultante in base64.
  4. Utilizzare il valore risultante come valore del parametro di richiesta Firma.

Quindi quello che ho è ....

private string GetSignature() 
{ 
    StringBuilder sb = new StringBuilder(); 
    sb.Append("GET\n"); 
    sb.Append("ec2.amazonaws.com\n"); 
    sb.Append("/\n"); 
    sb.Append(string.Join("&", parameters.OrderBy(vp => vp.Key, new CanonicalizedDictCompare()).Select(p => string.Format("{0}={1}", HttpUtility.UrlEncode(p.Key), HttpUtility.UrlEncode(p.Value))).ToArray())); 
    UTF8Encoding encoding = new UTF8Encoding(); 
    HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey)); 
    byte[] hash = signature.ComputeHash(encoding.GetBytes(sb.ToString())); 
    string result = Convert.ToBase64String(hash); 
    return result; 
} 

per completezza qui è l'implementazione IComparer ....

internal class CanonicalizedDictCompare : IComparer<string> 
    { 
    #region IComparer<string> Members 

    public int Compare(string x, string y) 
    { 
     return string.CompareOrdinal(x, y); 
    } 

    #endregion 
    } 

Per quanto mi riguarda posso dire che ho fatto tutto Ho bisogno di fare questo hash, ma continuo a ricevere un errore dal server che mi dice che la mia firma non è corretta. Aiuto ...

+1

La classe nell'esempio precedente avrebbe: utilizzando System.Security.Cryptography; Inoltre, per la descrizione di Amazon su come eseguire questa operazione (meno il calcolo dell'hash), vedere http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?rest-signature.html – adinas

risposta

7

Ok, ho capito ... L'UrlEncoding nella classe HttpUtility non è conforme allo schema di codifica Amazon .... grrr (in particolare il valore esadecimale dopo la% nell'utilità .NET. è in minuscolo, maiuscolo)

b. URL codificare il nome e valori di parametro in base alle seguenti regole:

  • Non URL codificare qualsiasi delle personaggi senza riserve RFC 3986 definisce. Questi caratteri senza prenotazione sono A-Z, a-z, 0-9, trattino (-), carattere di sottolineatura (_), punto (.) E tilde (~).
  • Percent codificare tutti gli altri caratteri con% XY, dove X e Y sono esagonali caratteri 0-9 e maiuscole A-F.

  • Percent codificare esteso caratteri nella forma% XY% ZA ....

  • Percent codificare lo spazio carattere 8 UTF-as% 20 (e non +, come schemi di codifica comune fanno).

Quindi dopo aver scritto un metodo rapido che codifica per questo schema, funziona perfettamente.

+0

Cheers per questo. Mi ha aiutato a farlo funzionare. Tuttavia ho trovato che ho ancora bisogno di codificare (almeno) il trattino. eseguendo una ricerca per parola chiave con un trattino in esso è stato generato un errore "Firma non valida". Non ho ancora testato altri caratteri speciali .. – Dermot

+0

Potresti condividere il codice per questo metodo? Lo apprezzerei davvero. –

+0

@AlirezaNoori sfortunatamente non ho più accesso ad esso. Sono passato dalla compagnia in cui l'ho fatto. Se ricordo bene, non è stato troppo difficile farlo, se si usa Reflector o IL/Spy si può vedere come è fatto nella classe HttpUtility e semplicemente in maiuscolo i caratteri esadecimali. –