cancel
Showing results for 
Search instead for 
Did you mean: 

Upgrading MD5 to new hash (SIM)

I am trying to upgrade from MD5 to the new hash method as described here:- https://developer.authorize.net/support/hash_upgrade

 

We are using SIM still do in addition I read the guide at https://support.authorize.net/s/article/Do-I-need-to-upgrade-my-transaction-fingerprint-from-HMAC-MD....  This second link seems to talk about generating the fingerprint with more values than the first link above does, more similar to how wo do MD5 currently.

 

 

I’m having trouble validating matching the value I submit to what is returned in the response.  Note: We need this to work with SIM as we can’t upgrade to the new API currently.

 

I am creating the hash using the new method and submitting it to the x_fp_hash during the post.  I assume my x_fp_hash is correct because I was previously getting error 99’s which is a fingerprint error, but no longer do.

 

The response comes back after doing the payment in the x_SHA2_Hash field.  It doesn't compare to x_fp_hash.  So I thought there is more to this.  Looking in the SIM guide, on page 73 at https://www.authorize.net/content/dam/authorize/documents/SIM_guide.pdf it mentions generating a hash from 30 fields.

 

I've done this and it still doesn't compare to x_SHA2_Hash.  So I assume I am doing something wrong, but not sure.  The documentation doesn't really give any examples of how to do this so I am uncertain what my issue is.

cwdsl
Contributor
55 REPLIES 55

"x_fp_hash" is not new, it has always been there, but previously is set to the MD5 hash.  With the the instruction from

https://support.authorize.net/s/article/Do-I-need-to-upgrade-my-transaction-fingerprint-from-HMAC-MD...

it says to switch to the Signature key compute the hash.  

 

The second part is the relay response also contains "x_MD5_Hash" and "x_SHA2_Hash" which is used to verify the response is from authorize.net. 

From what I understand "x_fp_hash" and "x_SHA2_Hash" need to be using the 128 bit Signature key to compute the hash.  But the hash I generate never match "x_SHA2_Hash" from the relay response.

I don't think I have a need for this but I've been working on it on and off for the past several hours. I think there may be some quirks that have to be worked out of authorize.net's end. The format of the signature key you need is an unsigned 8 bit integer, per their link. 

 

php is not amenable to using arrays in hashing, but I am not sure that you need an array. The documentation references "a byte version" of your signature key, and then references the array below. 

 

I am noticing that almost everyone who posts about this is using SIM or DPM. That explains something as I use Accept Hosted. This feature seems more tailored for your needs. If you're using SIM and are pulling your hair out right now, Accept Hosted is extremely easy to integrate and it took me about 10 minutes to get the sha512 hash integration working on the webhooks. 

 

I've fried quite a load of brain cells on this for no good reason at this point. If you're looking for a reason to upgrade this may be it. I figured it would take me 10 minutes like the webhooks did, and I figured why not validate the response on my refund API calls for such a small investment, although it is useless in all but the most remote circumstances I can imagine.   

 

Here is what I came up with. It works 100% with the PHP SDK and I believe will work for other methods with only slight modification.

 

https://community.developer.authorize.net/t5/Integration-and-Testing/Working-php-hash-verification/t...

@Renaissance, thanks for your example.  As you mentioned, I posted this question originally because it is SIM that I am trying to get working.  SIM from what I understand works differently to Accepted Hosted in how the hash works.  Your PHP SDK example looks great, but I need to get this working with SIM, at least temporarily.

 

@xero@dsstrainer... I am using VB.NET, but I'll try to explain what I've done so far.

 

Firstly, here is the old code, with MD5...

 

 

                    Dim Random As New Random
                    Dim Sequence = Random.Next(0, 1000)
                    Dim TimeStamp = CInt((DateTime.UtcNow - New DateTime(1970, 1, 1)).TotalSeconds)
                    Dim Fingerprint = GetAuthorizeNetMD5(anTransactionKey, anAPILoginID & "^" & Sequence & "^" & TimeStamp & "^" & Total.ToString("0.##") & "^USD")

 

 

When I post my initial transaction off to Authorize.Net, x_fp_sequence is assigned the Sequence, x_fp_timestamp is assigned the TimeStamp and x_fp_hash is assigned the Fingerprint variable.  

 

This is the GetAuthorizeNetMD5 function from the above example, which passes in the Transaction Key, API Login ID, Sequence, TimeStamp, Amount and Currency Code.

 

 

        Public Shared Function GetAuthorizeNetMD5(ByVal Key, ByVal Value)

            Dim HMACkey() As Byte = (New ASCIIEncoding()).GetBytes(Key)
            Dim HMACdata() As Byte = (New ASCIIEncoding()).GetBytes(Value)

            Dim TempHMACMD5 As New System.Security.Cryptography.HMACMD5(HMACkey)

            Dim HMAChash() As Byte = TempHMACMD5.ComputeHash(HMACdata)

            Dim Fingerprint = ""
            For i = 0 To HMAChash.Length - 1
                Fingerprint &= HMAChash(i).ToString("x").PadLeft(2, "0")
            Next

            Return Fingerprint

        End Function

 

 

The relay URL then receives back the MD5_Hash value.  We then compare this to the MD5 Hash Key similar to the code below...

 

 

        Public Shared Function AuthorizeNetMD5Matched(MD5HashKey As String, APILoginID As String, TransactionID As String, Amount As Decimal, MD5Hash As String) As Boolean

            Dim AmountStr As String = Amount.ToString("0.00")

            Dim Unencrypted = String.Format("{0}{1}{2}{3}", MD5HashKey, APILoginID, TransactionID, AmountStr)

            Dim MD5 = New System.Security.Cryptography.MD5CryptoServiceProvider()
            Dim Hashed = Regex.Replace(BitConverter.ToString(MD5.ComputeHash(ASCIIEncoding.[Default].GetBytes(Unencrypted))), "-", "")

            Return Hashed.Equals(MD5Hash)

        End Function

        If Not PaymentRepository.AuthorizeNetMD5Matched(MD5HashKey, APILoginID, TransactionID, Amount, MD5Hash) Then
            ' Code to abandon the transaction
        End If

 

 

So the above example passes into the AuthorizeNetMD5Matched function the MD5 Hash key and then using MD5 it then attempts to match the combination of the API Login, Transaction ID, Amount and the MD5_Hash returned.

 

Now the above code works with MD5 and the challenge is to know how to update this to work with SHA512.

 

Firstly, I decided to change how the fingerprint is generated...

 

 

Dim Fingerprint = GetAuthorizeNetHMACSHA512(anSignatureKey, anAPILoginID & "^" & Sequence & "^" & TimeStamp & "^" & Total.ToString("0.##") & "^USD")

 

The code above calls the function below, but instead of passing in the Transaction Key (like with MD5) I instead pass in the Signature Key instead, which I generated within my Sandbox account.

 

        Public Shared Function GetAuthorizeNetHMACSHA512(ByVal key As String, ByVal textToHash As String)

            If String.IsNullOrEmpty(Key) Then Throw New ArgumentNullException("HMACSHA512: key", "Parameter cannot be empty.")
            If String.IsNullOrEmpty(textToHash) Then Throw New ArgumentNullException("HMACSHA512: textToHash", "Parameter cannot be empty.")

            If Key.Length Mod 2 <> 0 OrElse Key.Trim().Length < 2 Then
                Throw New ArgumentNullException("HMACSHA512: key", "Parameter cannot be odd or less than 2 characters.")
            End If

            Try
                Dim k As Byte() = Enumerable.Range(0, Key.Length).Where(Function(x) x Mod 2 = 0).[Select](Function(x) Convert.ToByte(Key.Substring(x, 2), 16)).ToArray()

                Dim hmac As System.Security.Cryptography.HMACSHA512 = New System.Security.Cryptography.HMACSHA512(k)

                Dim HashedValue As Byte() = hmac.ComputeHash((New System.Text.ASCIIEncoding()).GetBytes(textToHash))
                Return BitConverter.ToString(HashedValue).Replace("-", String.Empty)


            Catch ex As Exception
                Throw New Exception("HMACSHA512: " & ex.Message)
            End Try


        End Function

The above function is a VB.NET equivelent of the example at https://developer.authorize.net/support/hash_upgrade/.

 

Now, I think this bit so far is correct.  I say this because I was getting error 99's on the fingerprint being invalid, but this now works fine.

 

What I am now struggling to do is compare the x_SHA2_Hash that is returned in the relay response to anything.  Page 73 of the SIM guide at https://www.authorize.net/content/dam/authorize/documents/SIM_guide.pdf implies you have to build a hash from 30 of the response fields and compare that, but I can't figure it out.

 

cwdsl
Contributor

@cwdsl, your link to Page 73 of the SIM guide was exactly what I needed. I was attempting to compare x_SHA2_Hash to the a hash of the value provided in the hash upgrade guide (of the format: ^ANet123^20987654321^9.99^). I needed to hash the 30 part carat separated value as specified in the pdf:

 

^x_trans_id^x_test_request^x_response_code^x_auth_code^x_cvv2_resp_code^x_cavv_response^x_avs_code^x_method^x_account_number^x_amount^x_company^x_first_name^x_last_name^x_address^x_city^x_state^x_zip^x_country^x_phone^x_fax^x_email^x_ship_to_company^x_ship_to_first_name^x_ship_to_last_name^x_ship_to_address^x_ship_to_city^x_ship_to_state^x_ship_to_zip^x_ship_to_country^x_invoice_num^

@tmnejp, so is that working for you?

 

I've tried comparing the x_SHA2_Hash to the hash of the 30 fields and they don't produce the same value for me.  Don't know what I am missing.  Are the 30 fields hashed using the Signature Key?

Yes it's hashed using the signature key. It also took a little effort to translate the Signature Key from a hex string to it's actual binary value (@Renaissance has a php solution to this in his/her example). In perl this was:

$key =  pack 'H*', $signature_key;

@tmnejp, okay thanks.  I still can't figure it out though.

 

I created a variable called TextToHash that is set to the response the 30 values, in this format...

 

^x_trans_id^x_test_request^x_response_code^x_auth_code^x_cvv2_resp_code^x_cavv_response^x_avs_code^x_method^x_account_number^x_amount^x_company^x_first_name^x_last_name^x_address^x_city^x_state^x_zip^x_country^x_phone^x_fax^x_email^x_ship_to_company^x_ship_to_first_name^x_ship_to_last_name^x_ship_to_address^x_ship_to_city^x_ship_to_state^x_ship_to_zip^x_ship_to_country^x_invoice_num^

 

Example: 

 

TextToHash = "^12345678901^false^1^ABCDEF^P^2^Y^CC^XXXX0027^64.88^^John^Smith^Main Street^London^^L11AA^United+Kingdom^^^^^^^^^^^^123^"

 

 

I then pass that these values into the HMACSHA512 function (as described at https://developer.authorize.net/support/hash_upgrade/)...

 

Example:

HashedStr = HMACSHA512(SignatureKey, TextToHash)

 

HashedStr does not equal the SHA2_Hash returned in the relay response, so I know I am missing something, but can't figure out what yet.

 

 

So wait, the the upgrade guide is stright up lying to us? We don't hash 3 values, we hash 30 of them? Where do you see hashing the 30 as a solution in their documentation?

lightwave365
Regular Contributor

@lightwave365, for the SIM integration method I believe so.  On Page 73 at https://www.authorize.net/content/dam/authorize/documents/SIM_guide.pdf it mentions 30 fields.

 

I presume the upgrade guide was only written with the newer API's in mind, but don't know that for a fact.