Laden...

Amazon MWS | RestSharp | The request signature we calculated does not match the signature you provid

Erstellt von d.jonas vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.367 Views
D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren
Amazon MWS | RestSharp | The request signature we calculated does not match the signature you provid

Hallo zusammen,

ich möchte über die Amazon MWS API Schnittstelle sämtliche Daten (bspw. Orders) abrufen. Leider bekomme ich jedoch jedesmal diese Fehlermeldung:

<?xml version="1.0"?>
<ErrorResponse xmlns="https://mws.amazonservices.com/Orders/2013-09-01">
  <Error>
    <Type>Sender</Type>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
  </Error>
  <RequestID>9f03f5b0-e4e4-4766-a554-00ff970b6b8c</RequestID>
</ErrorResponse>

Das wunder mich, da ich die selbe Signatur errechne, wie auch auf dem Amazon Scratchpad (https://mws.amazonservices.de/scratchpad/index.html) berechnet wird (Jedoch nur wenn ich den selben Zeitstempel eintrage, wie auch online generiert wird).

Folgend meine Methode und auch die Funktion zum errechnen der Signatur. Ich komme gerade nicht weiter und hoffe einer von euch weis woran es liegt.

Könnte es sein, dass es am RestCLient liegt? Dieser fügt standartmäßig noch 1-2 weitere Parameter hinzu?

public async void FetchOrders()
{
    RestClient client = new RestClient("https://mws.amazonservices.de");
    client.DefaultParameters.Clear();
    client.ClearHandlers();

    Dictionary<string, string> parameters = new Dictionary<string, string>();
    parameters.Add("AWSAccessKeyId", "xxxxxxxxxx");
    parameters.Add("Action", "ListOrders");
    parameters.Add("CreatedAfter", "2018-01-01T11:34:00Z");
    parameters.Add("MarketplaceId.Id.1", "A1PA6795UKMFR9");
    parameters.Add("SellerId", "xxxxxxxxx");
    parameters.Add("SignatureVersion", "2");
    parameters.Add("Timestamp", DateTime.UtcNow.ToString("s") + "Z");
    parameters.Add("Version", "2013-09-01");

    RestRequest request = new RestRequest("Orders/2013-09-01/", Method.POST);

    string signature = AmzLibrary.SignParameters(parameters, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
    request.AddParameter("Signature", signature);

    foreach (KeyValuePair<string, string> keyValuePair in parameters)
    {
        request.AddParameter(keyValuePair.Key, keyValuePair.Value);
    }

    IRestResponse result = await client.ExecuteTaskAsync(request);
}



public static class AmzLibrary
{
    public static string GetParametersAsString(IDictionary<String, String> parameters)
    {
        StringBuilder data = new StringBuilder();
        foreach (String key in (IEnumerable<String>)parameters.Keys)
        {
            String value = parameters[key];
            if (value != null)
            {
                data.Append(key);
                data.Append('=');
                data.Append(UrlEncode(value, false));
                data.Append('&');
            }
        }
        String result = data.ToString();
        return result.Remove(result.Length - 1);
    }

    public static String SignParameters(IDictionary<String, String> parameters, String key)
    {
        String signatureVersion = parameters["SignatureVersion"];

        KeyedHashAlgorithm algorithm = new HMACSHA1();

        String stringToSign = null;
        if ("2".Equals(signatureVersion))
        {
            String signatureMethod = "HmacSHA256";
            algorithm = KeyedHashAlgorithm.Create(signatureMethod.ToUpper());
            parameters.Add("SignatureMethod", signatureMethod);
            stringToSign = CalculateStringToSignV2(parameters);
        }
        else
        {
            throw new Exception("Invalid Signature Version specified");
        }

        return Sign(stringToSign, key, algorithm);
    }

    private static String CalculateStringToSignV2(IDictionary<String, String> parameters)
    {
        StringBuilder data = new StringBuilder();
        IDictionary<String, String> sorted =
              new SortedDictionary<String, String>(parameters, StringComparer.Ordinal);
        data.Append("POST");
        data.Append("\n");
        Uri endpoint = new Uri("https://mws.amazonservices.de/Orders/2013-09-01");

        data.Append(endpoint.Host);
        if (endpoint.Port != 443 && endpoint.Port != 80)
        {
            data.Append(":")
                .Append(endpoint.Port);
        }
        data.Append("\n");
        String uri = endpoint.AbsolutePath;
        if (uri == null || uri.Length == 0)
        {
            uri = "/";
        }
        data.Append(UrlEncode(uri, true));
        data.Append("\n");
        foreach (KeyValuePair<String, String> pair in sorted)
        {
            if (pair.Value != null)
            {
                data.Append(UrlEncode(pair.Key, false));
                data.Append("=");
                data.Append(UrlEncode(pair.Value, false));
                data.Append("&");
            }

        }

        String result = data.ToString();
        return result.Remove(result.Length - 1);
    }

    private static String UrlEncode(String data, bool path)
    {
        StringBuilder encoded = new StringBuilder();
        String unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" + (path ? "/" : "");

        foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data))
        {
            if (unreservedChars.IndexOf(symbol) != -1)
            {
                encoded.Append(symbol);
            }
            else
            {
                encoded.Append("%" + String.Format("{0:X2}", (int)symbol));
            }
        }

        return encoded.ToString();

    }

    private static String Sign(String data, String key, KeyedHashAlgorithm algorithm)
    {
        Encoding encoding = new UTF8Encoding();
        algorithm.Key = encoding.GetBytes(key);
        return Convert.ToBase64String(algorithm.ComputeHash(
            encoding.GetBytes(data.ToCharArray())));
    }
}
16.807 Beiträge seit 2008
vor 6 Jahren

Jetzt musst Du natürlich Glück haben und jemanden finden, der genau diese API kennt...

Hast Du mal auf GitHub nach vergleichbarem Code geschaut, wie dieser die Signatur ausrechnet?
Kann mir jetzt nicht vorstellen, dass es an der Amazon-Seite liegt.

Mal im Fiddler geschaut, ob die URL, die Du erzeugst, überhaupt gültig ist und so sind, wie Du es denkst?
Sieht etwas abenteuerlich aus, dass Du nicht einfach die Framework-Methoden für Queries und Parameter verwendest.

D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren

Hast Du mal auf GitHub nach vergleichbarem Code geschaut, wie dieser die Signatur ausrechnet?
Kann mir jetzt nicht vorstellen, dass es an der Amazon-Seite liegt.

Ich habe dazu kein GitHub Repo gefunden. Als Vorlage habe ich mir die Berechnung aus deren Beispiel geklaut https://developer.amazonservices.de/doc/bde/feeds/v20090101/cSharp.html. Ich denke auch nicht das diese falsch ist, da sowohl im C# als auch im Scratchpad die selbe Signatur errechnet wird.

Mal im Fiddler geschaut, ob die URL, die Du erzeugst, überhaupt gültig ist und so sind, wie Du es denkst.

Nein habe ich nicht. Wenn ich allerdings parameter vergesse zu übermitteln, erhalte eine dementsprechende Fehlermeldung. "Parameter Signatur fehlt", "Parameter Action fehlt; ist nicht gültig" etc..

Sieht etwas abenteuerlich aus, dass Du nicht einfach die Framework-Methoden für Queries und Parameter verwendest.

Was meinst du damit? Von welchen Framework-Methoden sprichst du? Ich benutze bspw. das Dictionary nur, da die Amazon Klasse ein Dict als Parameter zur Berechnung der Signatur verlangt.

D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren

Ich habe soeben mal diese Bibliothek (Chilkat C# library) getestet und musste feststellen, dass es damit funktioniert. Nun ist die Frage warum es bei mir nicht funktioniert?!

https://www.example-code.com/csharp/mws_list_orders.asp

Da dies mein erstes Projekt mit einer RestAPI ist, steh' ich ein bisschen auf dem Schlauch.

Danke im Voraus!

D
985 Beiträge seit 2014
vor 6 Jahren

Ich würde jetzt mal sagen, das Problem hat gar nichts mit REST & Co. zu tun.

Die Parameter müssen für die Signierung kodiert werden, was AmzLibrary.GetParametersAsString auch macht.

Der RestClient kodiert diese Parameter für den Request allerdings auch und genau da würde ich sagen liegt das Problem. Ein Leerzeichen kann als + oder als %20 kodiert werden. Der Sinn ist der gleiche. Für die Signierung ist das aber eben nicht das Gleiche.

2.207 Beiträge seit 2011
vor 6 Jahren

Hallo d.jonas,

du kannst auch mit Tools wie "Fiddler" nachschauen, welche HTTP-Requests rausgehen und die Requests mal vergleichen.

Gruss

Coffeebean

16.807 Beiträge seit 2008
vor 6 Jahren

I...musste feststellen, dass es damit funktioniert. Nun ist die Frage warum es bei mir nicht funktioniert?!

... deswegen hab ich Dich auf Fiddler hingewiesen. Es ist offensichtlich, dass es an Deinem Code liegt.

Dort kannst Du sehen - und vergleichen - was tatsächlich an die API geschickt wird und wo der Unterschied liegt.

Und ich hab Dich auf die Framework-Elemente verwiesen, da ich - wie Sir Rufo auch - die Vermutung hatte und habe, dass Du die Parameter in einem falschen Format an die API versendest.
Da ich selbst aber Deine HTTP Requests in Deinem Fiddler nicht sehe, muss man halt vermuten.
Analysieren und wirklich vergleichen kannst nur Du das.

D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren

Danke für eure Hilfe! Ich wollte gerade die beiden Requests miteinander vergleichen, jedoch wird der Traffic der "Chilkat" Bibliothek nicht aufgezeichnet, bei RestSharp geht das ohne Probleme. Gibt es hier spezielle Einstellungen vorzunehmen?

Folgendes habe ich schon probiert: http://docs.telerik.com/fiddler/Configure-Fiddler/Tasks/ConfigureDotNETApp Leider ohne Erfolg.



    GlobalProxySelection.Select = new WebProxy("127.0.0.1", 8888);


  <configuration>
   <system.net>
    <defaultProxy>
     <proxy bypassonlocal="false" usesystemdefault="true" />
    </defaultProxy>
   </system.net>
  </configuration>
D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren

Thread kann geschlossen werden!
Ich bedanke mich nochmal für eure Hilfe, dank Fiddler bin ich auf die Lösung gekommen.

Das Problem war das "/" am Ende:
Falsch

RestRequest request = new RestRequest("Orders/2013-09-01/", Method.POST);

Richtig

RestRequest request = new RestRequest("Orders/2013-09-01", Method.POST);

Falls jemand dennoch die Lösung weis, warum die externe Bibliothek nicht von Fiddler geloggt wird, bin ich gerne offen.

Gruß
d.jonas

16.807 Beiträge seit 2008
vor 6 Jahren

HTTPS? Muss im Fiddler extra aktiviert werden.
Dann kann Fiddler HTTPS aufbrechen.

D
d.jonas Themenstarter:in
21 Beiträge seit 2017
vor 6 Jahren

HTTPS? Muss im Fiddler extra aktiviert werden.

Ja das hatte ich gemacht, sonst wird mir auch die RestSharp Kommunikation nicht angezeigt. Dennoch wird mir die Kommunikation mit der Chilkat Bibliothek überhaupt nicht angezeigt.