Search code examples
delphibase64amazon-mwsdelphi-10.2-tokyo

Delphi - Amazon MWS API - How do derive the Base64 HMAC from the SHA 256 HMAC?


Using:

  • Delphi 10.2.3 Tokyo
  • IPWorks SSL, IPWorks Encrypt

I'm writing a Delphi app to get order list from Amazon MWS API.

I've followed their instructions here:

and here:

But I am stuck at the signature generation process, specifically generating the Base64 HMAC.

I'm using IPWorks SSL components (Hash component) and am able to generate the SHA 256 signature based on the inputs. Upto this step everything is okay.

Its the next step that I am unable / unsure how to perform.

I'm using the Amazon scratchpad to generate the request and am seeing the request details, and the signatures generated: both SHA 256 and then the Base 64.

My Delphi code does not produce a Base 64 string like the one generated in the scratchpad. Please see the attached screen capture (sensitive information has been redacted).

This is my Delphi code to convert the SHA 256 string to Base 64:

procedure TAppMain.Button1Click(Sender: TObject);
var
  s: String;
  b: TBytes;
begin
  s := '9660152e55a7178db9a9521cd80b7f4872f4be2290d1dd0f32205708f2e06589';

  s := TNetEncoding.Base64.Encode(s);

  // Above line produces:
  // OTY2MDE1MmU1NWE3MTc4ZGI5YTk1MjFjZDgwYjdmNDg3MmY0YmUyMjkwZDFkZDBmMzIyMDU3MDhmMmUwNjU4OQ==
  // which is not equal to:
  // lmAVLlWnF425qVIc2At/SHL0viKQ0d0PMiBXCPLgZYk=

end;

There is a StackOverflow question that is facing the same issue, and I have tried the suggestion in the answer to that question but I am still unable to derive the same result as the Amazon Scratchpad. Specifically:

The SHA256 Scratchpad shows is not the value you convert with base64. You have to convert the Hex-Value of the SHA256.

I've spent 2 whole days trying so many things to get the correct result, have read so many articles from Google search results, but I am still unable to find the answer.

Please see my other related question that shows the complete Delphi code, and the complete description of the problem that I am facing: Delphi - Calculating Amazon MWS signature

Can someone please help me!?

Amazon MWS Scratchpad


Solution

  • That's because you don't understand the whole point of Base64 at all, including how to write it correctly. Its main purpose is to carry 8bit data (i.e. whole bytes) safely thru 7bit (i.e. ASCII):

    • When encoding 6bits are taken and displayed as one letter.
    • When decoding one letter is taken and 6bit of original data are restored.

    Which is also the reason why encoding inflates the size by 1/3. When sending attachments in emails the former are stored in Base64, because emails are only 7bit safe. Which is the reason why sending a 4 MiB big picture ends up producing an email of at least 5.2 MiB.

    No, it makes no sense to Base64 encode something that is already ASCII and as such 7bit safe. Everybody should be alarmed when someone wants him to Base64 encode the text 9660152e55a7178db9a9521cd80b7f4872f4be2290d1dd0f32205708f2e06589.

    You want to encode bytes, not text. What you see is the hex representation of those bytes. You actually want this:

    var
      input: Array of Byte;
      output: String;
    begin
      SetLength( input, 32 );  // Represented in hexadecimal it would be a text of 64 characters
      input[0]:= $96;
      input[1]:= $60;
      input[2]:= $15;
      ...
      output:= TNetEncoding.Base64.Encode( input );
      if output<> 'lmAVLlWnF425qVIc2At/SHL0viKQ0d0PMiBXCPLgZYk=' then Halt();  // Expected output
    

    It's even more obvious when just trying to decode what you expect and just looking straight at it: once as if it's text, and once which byte values every character actually has (because you'll see it's not text at all). Text is not bytes, and vice versa. That's why Base64 exists. Not just for fun.