Hello,
I recently began working on integrating with the new Webhook API to subscribe to events. This is being integrated into an existing ASP.NET solution written in C#. I would like to validate webhook messages using the X-ANET-Signature header, but so far have not been able to produce a matching signature in my test setup. My code is as follows:
protected void Page_Load(object sender, EventArgs e)
{
string keytext = "MY AUTHORIZE.NET SIGNATURE KEY";
byte[] key = HexDecode(keytext);
HMACSHA512 hmacsha = new HMACSHA512(key);
byte[] hash = hmacsha.ComputeHash(Request.InputStream);
string computedHash = HashEncode(hash);
//compare with the X-ANET-Signature, they don't match
}
private static byte[] HexDecode(string hex)
{
var bytes = new byte[hex.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
}
return bytes;
}
private static string HashEncode(byte[] hash)
{
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
Any guidance would be much appreciated!
03-06-2017 07:54 AM
Hi @bschuller,
I can't tell exactly from your code where the problem might be, but here's some things to check.
Is Request.InputStream getting the body, the whole body, and nothing but the body? Add a line to output that and see if it's the entire JSON object, or something less/more.
Take whatever you've output from Request.InputStream and calculate the HMAC for it outside your code. Using a website like http://www.freeformatter.com/hmac-generator.html, you can put in the body that you've retrieved, and put in your signature key as the key. If you get a matching HMAC from that site, then you know your code is either mangling the signature key in the HexDecode function, or mangling the message body, or doing something else bad. If you don't get a matching HMAC from the web site, then maybe what you retrieved as the message body isn't complete or has something extra. Or, you're just using the wrong signature key.
Basically, it's just a strategy of breaking down what you're doing into steps, and checking the output at each point against a known working implementation.
Here's an example transaction I just tried. The X-Anet-Signature: header is:
sha512=9607C69D2734514554CE13D923D19939563F2FD6567D239F49F0DF0D08865632A0FB98061B4620CC52C47BB8BF6E2B69A15A67E4C98E81D7D65111076C515C6A
The body of the notification is:
{"notificationId":"f813b98f-3552-4eeb-ba40-acb4704eeba3","eventType":"net.authorize.payment.authcapture.created","eventDate":"2017-03-08T17:35:37.155999Z","webhookId":"63d6fea2-aa13-4b1d-a204-f5fbc15942b7","payload":{"responseCode":1,"authCode":"8VTPDC","avsResponse":"Y","authAmount":10.00,"entityName":"transaction","id":"60019392075"}}
My signature key is:
2679A5D5785C9B26CF921E1754765CDDB265C9C88400786923E5343B315954FAFB4B7BF608818F6A21766741E8637DC206F286C725908C777ED4127D8562163D
Using those values, I'm able to generate a HMAC that matches the one returned in the header.
Let us know how it works out.
03-08-2017 10:20 AM
Hi Aaron,
Thank you for the reply. I did get things sorted, so I thought I'd go ahead and post the details of the problem in case it helps anyone else. I had already followed the various debugging steps you mentioned, but it helped to go over to the hmac-generator page you sent, since it showed me the problem had to be in my conversion of the signature key into a byte array. I had mistakenly thought the signature key was to be read as pairs of characters making up a single hex digit, so I was parsing it like "26", "79", "A5"...etc (using your signature key as an example). So I was getting a byte array of length 64 instead of 128. Needed to just take the text of the key, do a simple "Encoding.ASCII.GetBytes(keytext)" instead, and it worked from that point forward.
Appreciate the help!
03-09-2017 12:51 PM
This post has been helpful, but I'm still stuck. I've spent far too long trying to figure this same thing out and I'm really close, thanks to your help. I'm just having a hard time with the final syntax. I'm working in VB and I'm confusing things I think with the conversion from C#.
Could you please post your final code?
Thanks!
- Jason
01-10-2018 03:18 PM
We do have a github project done by a developer for supporting webhooks
https://github.com/dns12345/AuthNet.WebHooks
Hope it helps !!
01-10-2018 10:23 PM - edited 01-10-2018 10:26 PM
@anuragg29 wrote:We do have a github project done by a developer for supporting webhooks
https://github.com/dns12345/AuthNet.WebHooks
Hope it helps !!
Thanks for your help... I checked out the source code on github, which is great if you speak C#. =) It did give me some ideas of how to implement things.
For struggling VB peeps trying to make sense of it all, I finally came up with some code after days of wrapping my head around it all. There may be a better/easier way to do this, however what I've got here seems to work. I hope this helps someone. Feel free to make it better.
Here are the steps:
1) From your Authorize.net account, create your Signature Key. (Account Settings > API Credentials & Keys). Save this somewhere... you're going to need it.
2) Create your webhook endpoint URL. (Business Settings > Webhooks). Make sure you keep it INACTIVE. When it's INACTIVE, a "Test Webhook" button is visible and enabled. When it's ACTIVE, the button disappears and you can't test it.
3) Create your webhook file in your application and copy and paste the code below.
4) Edit the code below where it reads <<put your Signature Key here>> and paste your Signature Key from Step 1 above.
5) Put your email address where it says <<your email address>> in both places.
6) You can now TEST your webhook and have your code email you to let you know it succeeded or failed. I simply emailed the result to myself so I could verify the X-ANET signature matched the generated hash.
7) You're welcome. ;-)
Imports System.IO
Imports System.Net.Mail
Imports System.Security.Cryptography
Partial Public Class webhook
Inherits System.Web.UI.Page
Public Shared Function ByteToString(ByVal buff As Byte()) As String
Dim sbinary As String = ""
For i As Integer = 0 To buff.Length - 1
sbinary += buff(i).ToString("X2")
Next
Return (sbinary)
End Function
Sub SubSendData(ByVal strEmail As String, ByVal strSubject As String, ByVal strBody As String)
'Add the constant ToAddress
Const ToAddress As String = "<<your email address>>"
'(1) Create the MailMessage instance (from/to)
Dim mm As New MailMessage(strEmail, ToAddress)
'(2) Assign the MailMessage's properties
mm.Subject = strSubject
mm.Body = strBody
mm.IsBodyHtml = False
'(3) Create the SmtpClient object
Dim smtp As New SmtpClient
'(4) Send the MailMessage (will use the Web.config settings)
'On Error Resume Next
smtp.Send(mm)
End Sub
Protected Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' Load Header collection into NameValueCollection object.
Dim coll As NameValueCollection
coll = Request.Headers
Dim loop1, loop2 As Integer
Dim arr1(), arr2() As String
Dim strSha512 As String = ""
' Put the names of all keys into a string array.
arr1 = coll.AllKeys
For loop1 = 0 To arr1.GetUpperBound(0)
arr2 = coll.GetValues(loop1)
' Get all values under this key.
For loop2 = 0 To arr2.GetUpperBound(0)
'the sha512 should be the last one in the array
'but to be sure let's check and exit when we find it
If InStr(arr2(loop2), "sha512") <> 0 Then
strSha512 = Server.HtmlEncode(arr2(loop2))
Exit For
End If
Next loop2
Next loop1
If strSha512 <> "" Then 'we found the X-ANET signature so continue
strSha512 = Replace(strSha512, "sha512=", "")
' now get the incoming stream body
Dim strInputStream As String
Dim streamReceive As Stream = Request.InputStream
Dim reader As StreamReader = New StreamReader(streamReceive, Encoding.UTF8)
strInputStream = reader.ReadToEnd
' calculate the fingerprint
Dim strKeyText As String = "<<put your Signature Key here>>"
Dim encASCII As ASCIIEncoding = New ASCIIEncoding()
Dim bytKeyText As Byte() = encASCII.GetBytes(strKeyText)
Dim hmacsha As HMACSHA512 = New HMACSHA512(bytKeyText)
Dim bytInputStream As Byte() = encASCII.GetBytes(strInputStream)
Dim bytHashInputStream As Byte() = hmacsha.ComputeHash(bytInputStream)
Dim strComputedHash As String = ByteToString(bytHashInputStream)
'now we have both the gateway X-ANET Signature (strSha512) , and our client generated value (strComputedHash) to compare
SubSendData("<<your email address>>", "webhook fired", strInputStream & vbNewLine & vbNewLine & strSha512 & vbNewLine & vbNewLine & strComputedHash)
If strSha512 = strComputedHash Then
'good to go so then do what we need
Else ' no match so do something else
End If
Else 'X-ANET signature (strSha512) is not found so abort
End If
End Sub
End Class
01-11-2018 11:43 AM
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for(var i=0; i<=buff.Length-1; i++)
{
sbinary += buff[i].ToString("X2");
}
return sbinary;
}
I ended having to use this instead of Convert.ToBase64String(payloadHashbytes).
byte[] keybytes = Encoding.UTF8.GetBytes(secret);
System.Security.Cryptography.HMACSHA512 hmac1 = new System.Security.Cryptography.HMACSHA512(keybytes);
byte[] payloadbytes = Encoding.UTF8.GetBytes(payload);
byte[] payloadhashbytes = hmac1.ComputeHash(payloadbytes);
//string hmacString = Convert.ToBase64String(payloadhashbytes); //this is not working...had to use below ByteToString method
string hmacString = ByteToString(payloadhashbytes);
return hmacString;
05-19-2022 09:11 AM
byte[] keybytes = Encoding.UTF8.GetBytes(secret);
byte[] payloadbytes = Encoding.UTF8.GetBytes(payload);
byte[] payloadhashbytes = hmac1.ComputeHash(payloadbytes);
//string hmacString = Convert.ToBase64String(payloadhashbytes); //this is not working...had to use below ByteToString method
string hmacString = ByteToString(payloadhashbytes);
return hmacString;
This is C# version of Janga's code above which is working for me.
private static string ByteToString(byte[] buff)
{
string sbinary = "";
for(var i=0; i<=buff.Length-1; i++)
{
sbinary += buff[i].ToString("X2");
}
return sbinary;
}
05-19-2022 09:15 AM