Crypto.HMAC JS to xojo help

Im given this as a js example of what im supposed to do:
function createHeaders(method, path) {
var timestamp = (new Date()).toISOString()
var toSign = method.toUpperCase() + " " + path + timestamp
var hash = crypto.createHmac(“SHA1”, secret)
hash.update(toSign)
var sig = hash.digest(“hex”);

var headers = {
    "X-HP-HMAC-Authentication": key + ":" + sig,
    "X-HP-HMAC-Date":  timestamp,
    "X-HP-HMAC-Algorithm": "SHA1"
}

return headers;

}

and so far I have this:

Var hash As String
var sig As String
hash = EncodeBase64(Crypto.HMAC(apikey, apisecret, Crypto.HashAlgorithms.SHA1))

MessageBox(hash)

and Im trying to figure out how to “update” the hash as in the js. Anyone know what the way to add the “hash.update(string)” functionality in xojo would be? It looks to me like in js you’re adding in another string with the update method of hash. Im not sure how to approach that. Then, it looks to me like they’re encoding it hex before adding the final hashed “sig” into the headers…

It’s just a difference in how the languages approach it. In JS, update is there so you can add more data in chunks before you extract the result. In Xojo, you simply get the results on the data.

It looks to me like both codes are doing the same thing.

You may need base64 URL encoding, which is different from normal base64 encoding. See EncodeBase64URLMBS in MBS Xojo Plugins.

Looking again, I think the JS code encodes in hex, not base64.

Now I have this and it runs but I get an error so I am working on just what gets added when for this HMAC process.

me.ClearRequestHeaders
var sendMethod as string = "GET"

Var hash As MemoryBlock
var path as string = "/externalApi/v1/Historic/OverallPerformance"
var sig As String
var d as DateTime = DateTime.Now(New TimeZone("Zulu"))
var timestamp as string =d.SQLDateTime
var toSign as string = "SHA1 "+path+timestamp +"Z"
hash = Crypto.HMAC(apikey, apisecret, Crypto.HashAlgorithms.SHA1)
sig=EncodeHex(Crypto.HMAC(hash, toSign, Crypto.HashAlgorithms.SHA1))

Self.RequestHeader("X-HP-HMAC-Authentication:") = apikey+":"+sig
Self.RequestHeader("X-HP-HMAC-Date:") = timestamp+"Z"
Self.RequestHeader("X-HP-HMAC-Algorithm:") = "SHA1"
var payload as string ="https://printos.api.hp.com/printbeat/externalApi/gsb/scitexJobs/records"
me.Send(sendMethod, payload)

If the api expects lowercase hex encoded signatures, then make sure to use .Lowercase on the strings. Many languages are case-sensitve, while xojo is not.

And are you sure this complies with the API’s expected values:

var timestamp as string = d.SQLDateTime // This is not standardized perhaps you need some ISO # conversion

Thanks Derk, I will try to lowercase the strings and look into the ISO. This is the HP PrintOne api and their instructions. I think I have the date the way they say they want it but …

  • x-hp-hmac-authentication - Key:Signature format where Key is the key created through PrintOS and Signature is a generated HMAC Hex String (see below).
  • x-hp-hmac-date - Current timestamp in ISO 8601 format (YYYY-MM-DDThh:mm:ss.sssZ). Note: Timestamp must be in UTC (GMT / Zulu) time as denoted by the trailing ‘Z’.
  • x-hp-hmac-algorithm (Optional) - Valid values are “SHA1” and “SHA256”. If the header is not provided the system will default to SHA1.
1 Like

this is driving me nuts. Heres what they tell me is working in C#:

static void Main(string[] args)
{
string json = “{” +
" “machineSns”: [" +
" “IL4600E028"” +
" ]," +
" “dateFrom”: “2021-01-01T00:00:00-00:00”," +
" “dateTo”: “2022-01-01T00:00:00-00:00”," +
" “startRow”: 0," +
" “endRow”: 99," +
" “selectedKpiTab”: “”" +
“}”;
var data = new StringContent(json, Encoding.UTF8, “application/json”);
var url = “https://printos.api.hp.com/printbeat/externalApi/gsb/scitexJobs/records”;
var client = new HttpClient();

        CreateHmacHeaders("POST", "/externalApi/gsb/scitexJobs/records", client);
        var response = client.PostAsync(url, data).Result;
        string result = response.Content.ReadAsStringAsync().Result;
    }

    private static void CreateHmacHeaders(string method, string path, HttpClient client)
    {
        string key = "<My key>";
        string secret = "<My secret>";
        string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
        string stringToSign = method + " " + path + timeStamp;
        HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
        byte[] bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
        string signature = BitConverter.ToString(bytes).Replace("-", string.Empty).ToLower();
        string auth = key + ":" + signature;

        client.DefaultRequestHeaders.Add("x-hp-hmac-authentication", auth);
        client.DefaultRequestHeaders.Add("x-hp-hmac-date", timeStamp);
        client.DefaultRequestHeaders.Add("x-hp-hmac-algorithm", "SHA1");
    }
}

}

…and heres what I have:

var json as string = “{” +_
" ““machineSns””: [" +_
" ““IL4600E028"” " +_
" ],” +_
" ““dateFrom””:2021-01-01T00:00:00-00:00," +_
" ““dateTo””:2022-01-01T00:00:00-00:00," +_
" ““startRow””: 0," +_
" ““endRow+””: 99," +_
" ““selectedKpiTab+””:"""" " +_
“}”
var sendMethod as string = “POST”
var hash As MemoryBlock
var path as string = “/externalApi/gsb/scitexJobs/records”
var sig As String
var d as DateTime = DateTime.Now(New TimeZone(“Zulu”))
var timestamp as string =d.SQLDateTime
var stringToSign as string = "SHA1 "+path+timestamp+“Z”

hash = Crypto.Hash(stringToSign, Crypto.HashAlgorithms.SHA1)
sig=EncodeHex(Crypto.HMAC(apisecret,hash, Crypto.HashAlgorithms.SHA1))
sig=sig.ReplaceAll("-","")
sig=sig.Lowercase

var auth as string = apikey + “:” + sig

me.SetRequestContent(json, “application/json”)
me.RequestHeader(“X-HP-HMAC-Authentication:”) = auth
me.RequestHeader(“X-HP-HMAC-Date:”) = timestamp+“Z”
'Self.RequestHeader(“X-HP-HMAC-Algorithm:”) = “SHA1”
var payload as string =“https://printos.api.hp.com/printbeat/externalApi/gsb/scitexJobs/records
me.Send(sendMethod, payload)

If I dont comment out the algorithm assignment, it gives me an error “HMAC algorith invalid” 400. Commented, it uses SHA1 as the default so that goes through and I get a 401 “Access denied for this resource” for whatever combination I think of sending the datat through. Anyone have any thoughts?

Shouldn’t it be:

var stringToSign as string = "SHA1 "+path+timestamp+"Z"
sig=EncodeHex(Crypto.HMAC(apisecret, stringToSign , Crypto.HashAlgorithms.SHA1))

?

1 Like

Also, the ReplaceAll( "-", "" ) line is not needed. It doesn’t hurt anything, it just won’t do anything.

DateTime.SQLDateTime is still wrong you need ISO 8601 there probably is a function on the forums for converting a datetime to iso 8601

1 Like

well, i tried it both ways; hashed and as a unhashed string and it complains if its not hashed. So, yes, it does have to be hash - which is stringToSign hashed. i *think

doesnt it remove the dash?

Xojo doesn’t include dashes.

1 Like

I did change the timestamp to this:

var timestamp as string =d.SQLDateTime
timestamp=timestamp.ReplaceAll(" ",“T”)

which seems to produce the properly formatted string. Unfortunately it still isnt letting me in so i have some type of malformation here.

OK, I was finally able to break through - as usual, self inflicted wounds but heres what worked below. Aside from some minor json formatting errors, the main culprit preventing auth was colons in the header names and the date formatting. For me, the challenging difference between the xojo hmac function and the rest of the languages is what threw me - all the others instantiate the hmac function with the secret and then hash the ‘codestring’ in a second operation, where we use both. Now that I understand it I actually think the xojo way is much easier - just not for a guy who plays programmer some days.

var json as string = “{” +_
" ““machineSns””: ["“IL4600E028"”]," +_
" ““dateFrom””:"“2021-01-01T00:00:00-00:00"”," +_
" ““dateTo””:"“2022-01-01T00:00:00-00:00"”," +_
" ““startRow””: 0," +_
" ““endRow+””: 99," +_
" ““selectedKpiTab””:"" “” " +_
“}”
var sendMethod as string = “POST”
var path as string = “/externalApi/gsb/scitexJobs/records”
var sig As String
var d as DateTime = DateTime.Now(New TimeZone(“Zulu”))
var timestamp as string =d.SQLDateTime
timestamp=timestamp.ReplaceAll(" “,“T”)
var stringToSign as string = sendMethod+” "+path+timestamp+“Z”
sig=Crypto.HMAC(apisecret,stringToSign, Crypto.HashAlgorithms.SHA1)
sig=EncodeHex(sig)
sig=sig.Lowercase

var auth as string = apikey + “:” + sig

self.SetRequestContent(json, “application/json”)
self.RequestHeader(“x-hp-hmac-authentication”) = auth
self.RequestHeader(“x-hp-hmac-date”) = timestamp+“Z”
self.RequestHeader(“x-hp-hmac-algorithm”) = “SHA1”
var payload as string =“https://printos.api.hp.com/printbeat/externalApi/gsb/scitexJobs/records
self.Send(sendMethod, payload)