Question

I'm attempting to use a 3rd party service that uses OAuth 1.0a and requires HMAC-SHA1 for the signature. I wrote a working version in C# and went to try to move it over to Delphi XE2. I immediately noticed something was wrong, the server was rejecting my calls saying that my "Signature is invalid." This is my method for generating a signature:

function TOAuth1.GenerateSignature(signatureBase, key : string) : string;
var
  hmacSha1 : TIdHMACSHA1;
  keyBytes, textBytes, hashedBytes : TBytes;
begin
  if(AnsiCompareText(fSignMethod,'PLAINTEXT') = 0) then
  begin
    Result := key;
  end
  else if(AnsiCompareText(fSignMethod,'HMAC-SHA1') = 0) then
  begin
    hmacSha1 := TIdHMACSHA1.Create;
    SetLength(keyBytes,Length(key));
    Move(key[1],keyBytes[0],Length(key));
    hmacSha1.Key := keyBytes;
    SetLength(textBytes,Length(signatureBase));
    Move(signatureBase[1],textBytes[0],Length(signatureBase));
    hashedBytes := hmacSha1.HashValue(textBytes);
    Result := EncodeBase64(hashedBytes,Length(hashedBytes));
    keyBytes := nil;
    textBytes := nil;
    hashedBytes := nil;

    hmacSha1.Free;
    hmacSha1 := nil;
  end;
end;

I couldn't spot anything that looked wrong to me, so I grabbed a signatureBase and key from my C# tests

signatureBase: POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%252Fsign-in-with-twitter%252F%26oauth_consumer_key%3DcChZNFj6T5R0TigYB9yd1w%26oauth_nonce%3Dea9ec8429b68d6b77cd5600adbbb0456%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318467427%26oauth_version%3D1.0

key: L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg&

Using these values in C#, I was given the signature F1Li3tvehgcraF8DMJ7OyxO4w9Y=, which was the expected value that Twitter gave me. However, in Delphi, I was given the signature /kov410nJhE6PTlk0R8bjP7JQq4=

In Delphi I called my function like this:

  signature := self.GenerateSignature('POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%252Fsign-in-with-twitter%252F%26oauth_consumer_key%3DcChZNFj6T5R0TigYB9yd1w%26oauth_nonce%3Dea9ec8429b68d6b77cd5600adbbb0456%26oauth_signature_method'+'%3DHMAC-SHA1%26oauth_timestamp%3D1318467427%26oauth_version%3D1.0','L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg&');
  assert(AnsiCompareStr(signature,'F1Li3tvehgcraF8DMJ7OyxO4w9Y=') = 0,'Signature generated is invalid');

So my question would be this: Am I using the HMAC-SHA1 incorrectly? If so, what steps should I take to fix it? If not, then is the implementation of HMAC-SHA1 in Indy done incorrectly? If so, is there an easy to use (preferably free) unit that can handle it correctly? Or is there something else altogether wrong here?

Was it helpful?

Solution

Looks like the common Unicode string misconception. Since Delphi 2009, string maps to UnicodeString, encoded with UTF-16, and no longer to AnsiString. So,

Move(key[1],keyBytes[0],Length(key));

will probably only copy the first half of the string with 2 bytes per character. Use UTF8Encode to convert the key to UTF8 first, and copy that into keyBytes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top