1

I am trying to encrypt 3 different elements into an authentication token for a http headers for an API. This API is built into Google Sheets, and I can't use anything else at the moment.

The authentication token requires 4 parts:

  1. API Key
  2. Timestamp in UTC format
  3. API Action
  4. API Secret Key

In the format of API KEY:TIMESTAMP:API ACTION:API Secret Key

For the purposes of this example, let's say that the

  1. API key is test123,
  2. UTC Date: Thu, 14 Apr 2011 22:44:22 GMT
  3. API action is 'ledger'
  4. API Secret key is UAV213Q

When I tested the example using following format "test123:Thu, 14 Apr 2011 22:44:22 GMT:ledger:UAV213Q" in python I got the result 15594d1f608134cbfa3075ecda4664519cd198738b8f5c3ffa2c95272b854199

This is the python script I used

def sha256():
    # tested on Python 3.8.5
    from urllib import parse, request
    import hashlib
    import datetime
    from time import strftime, gmtime

    # credentials and request params
    my_merchant_id = 'apikey'
    api_token = 'test123'
    api_secret_key = 'UAV213Q'
    my_timestamp = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
    api_version = 2.9
    action_verb = 'ledger'

    # set up request params
    data = parse.urlencode({'merchantId': my_merchant_id, 'token': api_token,
                        'version': api_version, 'action': action_verb})

    # authentication
    sig = api_token + ':' + my_timestamp + ':' + action_verb + ':' + api_secret_key
    sig_hash = hashlib.sha256(sig.encode('utf-8')).hexdigest()
    my_headers = {'x-ShareASale-Date': my_timestamp,
                'x-ShareASale-Authentication': sig_hash}

    print(sig_hash)

I've tried using solutions from the following other StackOverFlow questions How do I get Google Apps Script to do SHA-256 encryption?, sha3-256 of a cell text in Google Spreadsheet, all the suggestions.

However, I keep getting the error message "This function is not allowed to reference a cell with NOW(), RAND(), or RANDBETWEEN()."

I have tried referencing a cell that is indirectly references the NOW() by having NOW() in A1 and having B1 =A1, I have even tried to convert it to text by using the TEXT().

The API key needs to have the timestamp to function. I was thinking about having that calculated in the App script itself, since it is a known constant. For example within the encryption script it would hardcode the api token, call the timestamp in utc format and hardcode the api secret key in the correct format and the maybe just the function to add the action so I can make change that so it would be sha256(ledger) and it would incorporate that into the encryption

Jknight
  • 105
  • 11
  • Can you provide the sample input and output values you expect? Unfortunately, from your question, I couldn't understand about them. I apologize for this. – Tanaike Aug 26 '20 at 07:25
  • @Tanaike edit to include formatting which showcase sample inputs. i expect an output like this 78D54A3051AE0AAAF022AA2DA230B97D5219D82183FEFF71E2D53DEC6057D9F1 – Jknight Aug 26 '20 at 07:30
  • Thank you for replying. In your situation, can you provide the specification for creating the signature? Because I cannot understand about the logic for retrieving the output of `78D54A3051AE0AAAF022AA2DA230B97D5219D82183FEFF71E2D53DEC6057D9F1` from the values of `API key is test123`, `API action is 'ledger'`, `API Secret key is UAV213Q` and `In the format of API KEY:TIMESTAMP:API ACTION:API Secret Key`. And also, I think that in order to retrieve your expected result, the sample value of "TIMESTAMP" is required. Also can you provide it? – Tanaike Aug 26 '20 at 07:36
  • @Tanaike thanks, I'm still new to this. I've edited the question with the sample format and the expected output that I got using python. Ignore the one I gave you before it wasn't using the example inputs – Jknight Aug 26 '20 at 07:51
  • Thank you for replying. Can you add the script of python to your question? I would like to try to understand the specification for creating the signature from your python script. – Tanaike Aug 26 '20 at 08:07
  • @Tanaike added python – Jknight Aug 26 '20 at 08:35
  • Thank you for replying and adding the sample python script. From your python script, I proposed the converted Google Apps Script as an answer. Could you please confirm it? In my environment, when `test123:Thu, 14 Apr 2011 22:44:22 +0000:ledger:UAV213Q` is used for the proposed Google Apps Script, I could confirm that the result value is the same with that of your python script. – Tanaike Aug 26 '20 at 09:02

1 Answers1

4

How about this answer?

Modification points:

  • When I saw your python script, I confirmed that the specification shown in your question is different from that of the python script.
    • It seems that Thu, 14 Apr 2011 22:44:22 GMT is Thu, 14 Apr 2011 22:44:22 +0000
    • It seems that it is required to use "SHA_256" of the digest.

Sample script:

When your python script is converted to Google Apps Script, it becomes as follows. Please copy and paste it to the script editor and run the function myFunction at the script editor. By this, you can see the result value at the log.

function myFunction() {
  const api_token = 'test123';
  const api_secret_key = 'UAV213Q';
  const my_timestamp = 'Thu, 14 Apr 2011 22:44:22 +0000';
  const action_verb = 'ledger';

  const value = `${api_token}:${my_timestamp}:${action_verb}:${api_secret_key}`;
  const bytes = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, value);
  const res = bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('');
  console.log(res)
}

Result:

When test123:Thu, 14 Apr 2011 22:44:22 +0000:ledger:UAV213Q is used for above Google Apps Script and your python script, both results are the same as follows.

8c3a6873fe71c402dc1e3ca7bc828712e3dfb7a66ed09feeeca2152dd809df81

Reference:

Added:

Answer for additional question 1:

When you want to retrieve the date string like Thu, 14 Apr 2011 22:44:22 +0000, please use the following script.

const my_timestamp = new Date().toUTCString().replace("GMT", "+0000");

Answer for additional question 2:

When you want to retrieve the value as the upper case, please use the following script. But when I tested your python script, the result value is the lower case. So please be careful this.

function myFunction() {
  const api_token = 'test123';
  const api_secret_key = 'UAV213Q';
  const my_timestamp = 'Thu, 14 Apr 2011 22:44:22 +0000';
  const action_verb = 'ledger';

  const value = `${api_token}:${my_timestamp}:${action_verb}:${api_secret_key}`;
  const bytes = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, value);
  const res = bytes.map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2)).join('').toUpperCase();
  console.log(res)
}
  • In this case, 8C3A6873FE71C402DC1E3CA7BC828712E3DFB7A66ED09FEEECA2152DD809DF81 is obtained.

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • I can confirm that i got the same answer, however, i need my_timestamp to by dynamic to take the the time in that format at the time it is processed. Also when I converted it to the real values. I had to change the timestamp from +0000 to GMT for it to get the same result that the api showcases, but the values from your script is all lower case whereas theirs is uppercase. Does that make a difference? – Jknight Aug 26 '20 at 09:11
  • @Jknight Thank you for replying. About your additional 2 questions, I updated my answer. Could you please confirm it? – Tanaike Aug 26 '20 at 11:35
  • @Jknight By the way, I might not be able to understand about `I had to change the timestamp from +0000 to GMT for it to get the same result that the api showcases, but the values from your script is all lower case whereas theirs is uppercase. Does that make a difference?`. If my understanding is not correct, can I ask you about the detail of it? And `new Date().toUTCString()` returns the format like `Thu, 14 Apr 2011 22:44:22 GMT`. How about this? I have only the information of your python script and no information about the API you are using. So please also be careful this. – Tanaike Aug 26 '20 at 11:48
  • this worked thanks. Now I have to get the rest of the API to work, but I think the encryption is correct. Since timestamp is part of the encryption it's difficult to run the python and the app script at the exact same time on the same machine. But look good. thanks – Jknight Aug 26 '20 at 17:44
  • This is the rest of the API code for the in python, `# execute request call = request.Request('https://shareasale.com/w.cfm?%s' % data, headers=my_headers) try: return_result = request.urlopen(call).read() # output results to console or write to file # f = open('out.txt', 'w') # print >> f, return_result # f.close() print(return_result) except Exception as inst: print(inst)` – Jknight Aug 26 '20 at 17:50
  • @Jknight Thank you for replying and adding more information. I'm glad your issue was resolved. Thank you, too. – Tanaike Aug 26 '20 at 22:35