Andre Bulatov Andre Bulatov - 5 months ago 22
JSON Question

Encoding PHP POST response's JSON body into HMAC SHA256, and then into Base64

How to receive raw JSON response from HTTP POST webhook?






I am working with an API and to verify that the POST to the webhook is indeed from the appropriate company API, they suggest this method:


To allow a client to verify a webhook message has in fact come from SIGNIFYD, an X-SIGNIFYD-SEC-HMAC-SHA256 header is included in each webhook POST message. The contents of this header is the Base64 encoded output of the HMAC SHA256 encoding of the JSON body of the message, using the team's API key as the encryption key. To verify the authenticity of the webhook message, you should calculate this value yourself and verify it equals the value contained in the header.


For the test environment, the "secret" key is
ABCDE
instead of the "Team API key."

I am receiving it in PHP like so:

<?php

// Get relevant Signifyd custom headers to be used for verification
$header_sig_topic = $_SERVER['HTTP_X_SIGNIFYD_TOPIC'];
$header_sig_sec_hmac = $_SERVER['HTTP_X_SIGNIFYD_SEC_HMAC_SHA256'];

// Get POST body
$webhookContent = "";
$webhook = fopen('php://input' , 'r');
while (!feof($webhook)) {
$webhookContent .= fread($webhook, 4096);
}
fclose($webhook);

?>


then I am processing it into the hash like so:

<?php
$sig_ver_sha = hash_hmac('sha256', $webhookContent, $secret);
$sig_ver_hash = base64_encode( $sig_ver_sha );
?>


However, I am going wrong somewhere, because the hash I calculate is

OTc1YzExZDY2ZTE1MTVmYmJmNWNhNDRhNWMxZGIzZDk0NmM3OGE4NDU2N2JkYTJmZDJlYWI0ODRhNjlhNTdiYg==


while the header for an identical sample response header always comes with

W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=


I thought I was getting the JSOn body wrong somehow so I've tried every combination of json_encode and json_decode but nothing helps, my hash never matches.

I've also tried using
$webhookContent = json_decode(file_get_contents('php://input'), true);
to store the POST body but that just comes up empty ($_POST doesn't work either).

Am I doing something else wrong other than receiving the JSON?




The JSON that comes as the body of the test response which always comes with
W+D70ded8u5DG7P4BcG0u2etvAqQZvxz70Q4OXh0vlY=
as the hash key to be used for verification:


{ "analysisUrl": "https://signifyd.com/v2/cases/1/analysis",
"entriesUrl": "https://signifyd.com/v2/cases/1/entries", "notesUrl":
"https://signifyd.com/v2/cases/1/notes", "orderUrl":
"https://signifyd.com/v2/cases/1/order", "guaranteeEligible":false,
"status":"DISMISSED", "uuid":"709b9107-eda0-4cdd-bdac-a82f51a8a3f3",
"headline":"John Smith", "reviewDisposition":null, "associatedTeam":{
"teamName":"anyTeam", "teamId":26, "getAutoDismiss":true,
"getTeamDismissalDays":2 }, "orderId":"19418",
"orderDate":"2013-06-17T06:20:47-0700", "orderAmount":365.99,
"createdAt":"2013-11-05T14:23:26-0800",
"updatedAt":"2013-11-05T14:23:26-0800",
"adjustedScore":262.6666666666667, "investigationId":1,
"score":262.6666666666667, "caseId":1,
"guaranteeDisposition":"APPROVED"}



If it helps to see where I'm going wrong, an example is provided but it's in Python:

Mac sha256HMAC = javax.crypto.Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(teamAPIKEY.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
String encodedHMAC256 = Base64.encodeBase64String(sha256HMAC.doFinal(jsonBody.getBytes("UTF-8")));

Answer

My error was in simply not specifying the $raw_output parameter of the hash_hmac() function as true.

raw_output
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.

So, since I wasn't specifying $raw_output as true, I was getting hexits instead of raw binary output, which looked like this: 975c11d66e1515fbbf5ca44a5c1db3d946c78a84567bda2fd2eab484a69a57bb.

Comments