0

Using: Delphi 10.2.3 Tokyo IPWorks SSL and IPWorks Encrypt components

I am trying to use the Amazon MWS API from Delphi to get a list of orders, but am unsuccessful in making the request to Amazon MWS. Its mandatory that I use IPWorks components, as required by my client. The response from the API says that the signature is not correct:

Sender SignatureDoesNotMatch 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.

Obviously the issue has something to do with calculating the signature, encoding it to Base64, or something wrong with the request itself. I think its the encoding to Base64 that is not working for me. Maybe something to do with Delphi Unicode and UTF-8 encoding.

I've spent a whole day trying to figure this out and am stuck, so am posting it here. I hope someone can help. Below is my code:

On the form I have 2 components: TipwHTTP and TipcHash

object HTTPS: TipwHTTP
  FollowRedirects = frAlways
  SSLCertStore = 'MY'
  OnTransfer = HTTPSTransfer
end

object HashMaker: TipcHash
  Algorithm = haHMACSHA256
  EncodeHash = True
end


function GetISO8601DateTime_URLEncodedStr(const ADateTime: TDateTime): String;
var
  d: String;
  l: Integer;
begin
  d := DateToISO8601(TTimeZone.Local.ToUniversalTime(ADateTime), True);
  l := Length(d);
  d := Copy(d, 1, l - 5) + 'Z'; // remove the milliseconds part

  Result := TNetEncoding.URL.Encode(d);
end;

const
  DOMAIN_NAME = 'https://mws.amazonservices.com/Orders/2013-09-01';
var
  sl: TStringList;
  param, signature, aurl: String;
begin
  sl := TStringList.Create;
  try
    sl.Add('AWSAccessKeyId=' + edtAwsAccessKey.Text); { Index: 0 }
    sl.Add('&Action=ListOrders'); { Index: 1 }
    sl.Add('&CreatedAfter=' + GetISO8601DateTime_URLEncodedStr(dtpOrdersCreatedFrom.Date)); { Index: 2 }
    sl.Add('&MWSAuthToken=' + edtMarketplaceID.Text); { Index: 3 }
    sl.Add('&MarketplaceId.Id.1=' + edtMarketplaceID.Text); { Index: 4 }
    sl.Add('&SellerId=' + edtSellerID.Text); { Index: 5 }
    sl.Add('&SignatureMethod=HmacSHA256'); { Index: 6 }   // <---- Insert Signature here
    sl.Add('&SignatureVersion=2'); { Index: 7 }
    sl.Add('&Timestamp=' + GetISO8601DateTime_URLEncodedStr(Now)); { Index: 8 }
    sl.Add('&Version=2013-09-01'); { Index: 9 }

    param := StringReplace(sl.Text, #13#10, '', [rfReplaceAll]);

    HashMaker.Key := edtSecretKey.Text;
    HashMaker.Algorithm := haHMACSHA256;
    HashMaker.InputMessage := 'POST\n' + 'mws.amazonservices.com\n' + '/Orders/2013-09-01\n' + param;
    HashMaker.ComputeHash;
    signature := HashMaker.HashValue;

    SetStatus('Signature1: ' + signature);

    signature := TNetEncoding.Base64.Encode(TEncoding.UTF8.GetString(BytesOf(signature)));

    signature := TNetEncoding.URL.Encode(signature);

    SetStatus('Signature2: ' + signature);

    sl.Insert(6, '&Signature=' + signature);
    param := StringReplace(sl.Text, #13#10, '', [rfReplaceAll]);

    SetStatus('param: ' + param);

  finally
    sl.Free;
  end;

  with HTTPS do
  begin
    ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
    Accept := 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8';
    Timeout := 0;

    aurl := DOMAIN_NAME + '?' + param;
    Post(aurl);
  end;

end;

I would really appreciate if someone can help me solve this.

-Steve

Steve F
  • 1,527
  • 1
  • 29
  • 55
  • What does the documentation instruct you to? At which point to do you fail? Is Base64 encoding just a wild guess from you? We are not Amazon - ask them for support. – AmigoJack Oct 29 '21 at 14:07
  • @AmigoJack Yes I think its at the point of Base64 encoding. – Steve F Oct 29 '21 at 14:16
  • @AmigoJack I'm pretty sure that the issue has something to do with the UTF-8 vs Unicode. – Steve F Oct 29 '21 at 15:19
  • You need to encode the signature as BASE64 followed by URI Encode if using it as parameter in the URL (since BASE64 uses + = and /). Some date formats need to be URI Encoded as well. – Brian Oct 29 '21 at 17:13
  • @Brian Great. I will try that. I was not doing the last bit ie. URI encode. Thanks! – Steve F Oct 29 '21 at 17:16
  • @Brian: I tried and not working. I've modified the code in my question to illustrate this. – Steve F Oct 29 '21 at 17:19
  • Base64 the raw binary signature bytes not some UTF8 string or any string version of it. Ex something like `signature := TNetEncoding.Base64.Encode(HashMaker.HashValueB);` – Brian Oct 29 '21 at 18:06
  • Might also need to turn off hex encoding the hash with `EncodeHash = false`. Not sure if it effects HashValueB or not (don't have IPWorks Encrypt myself to test). – Brian Oct 29 '21 at 18:15

0 Answers0