[CryptoAPI] API using 'MAC algorithm: HMAC-SHA256' as authentication

Forge Component
(22)
Published on 3 Mar by Ricardo Silva
22 votes
Published on 3 Mar by Ricardo Silva

Hi there!

I'm having some trouble authenticating to a payment system API while using your component to generate a base64 encoded HMAC SHA256 signature : https://epayments-api.developer-ingenico.com/s2sapi/v1/en_US/json/authentication.html

As you can see the authentication requires an annoying authorization header.

Authorization="GCS <authorizationType>:<apiKeyId>:<base64 encoded HMAC signature>

Example of full authorization header: (marked in bold is the generated HMAC signature)

Authorization="GCS v1HMAC:5e45c937b9db33ae:J5LjfSBvrQNhu7gG0gvifZt+IWNDReGCmHmBmth6ueI="


How the HMAC signature should be calculated: 

(input: <signature-contents> & <secretApiKey>)


AuthorizationType and APIkey i do already have, but its the base 64 encoded HMAC signature that's the issue.

This is a signature content example : (enters are included!! )

"
GET

Fri, 06 Jun 2014 13:39:43 GMT
/v1/9991/tokens/123456789

"

and this is the secretAPIKey (password in your component)

I42Zf4pVnRdroHfuHnRiJjJ2B6+22h0yQt/R3nZR8Xg=

Example of the generated HMAC SHA256 signature should then become :

J5LjfSBvrQNhu7gG0gvifZt+IWNDReGCmHmBmth6ueI=


The problem is that I try to accomplish the same generated HMAC SHA256 signature result using the same example, but I'm getting a different one..
This is my code in Outsystems: (it's stated that enters should be included when generating the signature)



And for some reason I'm not getting: 

J5LjfSBvrQNhu7gG0gvifZt+IWNDReGCmHmBmth6ueI=

But I'm getting a different one:


Any idea what I'm doing wrong? Is he ignoring the 'enters' in my string?

Greetings,
Niels F


Hey!

Is the password on the first attempt exactly equal to the one on the second attempt? I have noticed you are missing a = on your second attempt.

Yes, the = is there at the end, but isnt in the screenshot :p

Still didn't find a solution :/

Solution

Hello Niels,

I've looked a bit into this and the answer isn't very simple.

Using python with the below code I am able to replicate the first couple of signatures (but not the last):


import hmac
import hashlib
import base64

apikey = '5e45c937b9db33ae'
secretKey = 'I42Zf4pVnRdroHfuHnRiJjJ2B6+22h0yQt/R3nZR8Xg='

def ingenico(data, key):
        digest = hmac.new(key, digestmod=hashlib.sha256, msg=data).digest()
        return base64.b64encode(digest)

signed_data = """GET

Fri, 06 Jun 2014 13:39:43 GMT
/v1/9991/tokens/123456789
"""


signed_data2 = """GET

Fri, 06 Jun 2014 13:39:43 GMT
/v1/consumer/ANDR%C3%89E/?q=na me
"""


signed_data3 = """DELETE
application/json
Fri, 06 Jun 2014 13:39:43 GMT
x-gcs-clientmetainfo:value
x-gcs-customerheader:some other data
x-gcs-servermetainfo:value
/v1/9991/tokens/123456789
"""

print 'Authorization="GCS v1HMAC:{}:{}"'.format(apikey, ingenico(signed_data, secretKey))
print 'Authorization="GCS v1HMAC:{}:{}"'.format(apikey, ingenico(signed_data2, secretKey))
print 'Authorization="GCS v1HMAC:{}:{}"'.format(apikey, ingenico(signed_data3, secretKey))


So ... what does this mean for what you are doing?


1) new lines in Windows (and thus, the OutSystems Platform) are two characters long (\r\n) instead of one (\n)

2) you're adding way too many new lines (for example, the one before the GET method is not required, or the one at the end of the signed data)

3) you shouldn't pass the secretKey through my "password" parameter, as that will process it in a way that is incompatible with this algorithm.

4) from my experience, copy-pasting the values from the site will leave them with extra whitespace at the end which I had to remove. For example, it's GET\n, not GET \n.


So, what do you need to do in order to get this right in low code?

1) properly build the strings to be signed. You'll need to use Chr(13) to separate the lines instead of the built-in editor new line

2) Don't use the password version of ComputeMac (in Crypto API prior to 2.0), use the KComputeMac with a BinaryData.TextToBinaryData(secretKey) for passing the key


This should get you there with the simple examples.


However, I do not recommend implementing this kind of algorithms in low-code. Since they are very dependent on low level details and manipulation, I would implement something in an extension and then use that in your low-code. With Low-Code the "process headers" part will be another pain for you to overcome (since it's probably not even that easy to do in "high"-code).


Since you're integrating with their API and they do have an SDK, an even better approach would probably be to abstract and encapsulate that API in an extension instead of in low-code. This would mean that you do not have to deal with the gritty details of the signature algorithm, and if something changes you probably just have to replace the SDK in your extension.


Best of luck in integrating with this API, whichever approach you choose to follow.

Solution

Thank you very much for your answer! Creating an extension was indeed one of our solutions.

Hi Ricardo!


One more question.

Since in Extensions, we are not allowed to use app.config (it get's overweitten) How would you initialize the SDk?

https://epayments.developer-ingenico.com/documentation/sdk/server/dotnet/#initialization


App;config is required :/

You might be able to use Factory Configuration for that.