cancel
Showing results for 
Search instead for 
Did you mean: 

SHA-512 in Perl

Our authorize.net processing is done in some OLD perl cgi code - any perl programmers out there?

 

We are trying to convert to the SHA-512 hashing.  Our current processing uses MD5, via the perl module Digest::MD5.

I use LWP::UserAgent to POST directly to the secure.authorize.net gateway transact dll URL.

What I get returned is an array of values. The MD5 hash is currently in the 38th array element. Authorize.net has been unable to tell me where I can find the returned SHA-512 hash value to compare to what I am generating in the program.

 

For my test:

I changed it to use Digest::SHA for the hashing. I generated the signature key and have it stored in hex in our database.  

 

 

my $sha512_string = '^' . $auth_net_login_id . '^' . $tranid . '^' . $grandtotal . '^';


my $key = pack 'H*', $sig_key;  ##to convert the store hex value to binary - as recommended here


my $sha512 = Digest::SHA->new;
my $sent_sha512_hash = $sha512->hmac_sha512($sha512_string, $key);

 

When I display that value, it just shows a bunch of weird characters on the screen - I don't know if that's expected or not. I am only displaying it to compare to what comes from authorize.net.

 

When the values are returned from Authorize.net (in the array), I display all the elements. There is a value in element 68 that looks like a hex value but that isn't what is in the hash that I generated.

 

So, isn't the hash returned from Authorize.net in the array? If not, then how do I obtain it using the methods we currently have in place? I don't consider this as using the API.   Or is the problem that I am hashing it wrong on my end?

 

I obtained the perl code for our current processing via Authorize.net MANY years ago from one of their perl customers. It has worked fine ever since. I do not have the knowledge, experience or brain power to change the whole process, unless someone could provide all the perl code (I know that's asking a lot). I also have a general knowlege of php but unfortunately the examples on this forum are too different from our perl process to be able to correlate the two.

 

I hope someone can help!   Thanks in advance!

 

smorrow123
Contributor
76 REPLIES 76
And here’s a possibly dumb question- does the api response return any values that are not in your request, other than the MD5 and that hex value you refer to that is the sha2? It seems like you would need the transaction id and possibly just a few others from the response and you could just use the same values that you submit in your request. If the api is just spitting back out verbatim values for everything you send it, no need for acrobatics to extract from that when you’ve got the same values handy.

@Renaissance

i understand what you’re saying, but yes, it returns a fair number of other things that I don’t send in the original request. And the values are not easily identifiable as far as what field name they represent. When I get back to this, I might re-read the full SIM documentation to see if I can figure out another way to do the POST. Another developer posted his perl cgi code where he accessed the field names and values like regular query params. I might check with him to see the code that he used to get to that point. I’ve never initiated 2 queries in the same script but I don’t know why it can’t be done as long as I call it by another name.  I would REALLY like to implement the API so it would all be so much easier, well documented, and supported by authorize.net. But I just don’t feel qualified to do that. 

 

I’m not giving up —- yet.  But I’m only part time, have had limited exposure to programming over the last few years.  PLUS I’m old as dirt.  I could handle these marathons when I was younger, but no longer. 

 

 

 

 

Yeah I think Perl should have the capability to make multiple arrays out of the same variable.  This is actually pretty easy now that I look at it a 3rd time. I’ll do it one piece at a time-

 

1|1|1|(TESTMODE) This transaction has been approved.|000000|P|0|8478||39.00|CC|auth_capture|10001253|

 

I think the first set of three 1’s is the response code and response reason code. (TESTMODE)  is x_test_request. Then we have response reason text, x_auth_code, x_avs_code, x_trans_id, x_invoice_num, a | representing the empty description field, then x_amount, x_method, x_type

 

The 10001253 maybe you have an idea of. It could be numerous things. Maybe customer Id.

 

|Sharon|Shatest||123 Somewhere Street|Anywhere|PA|19666|US|||sharon.shatest@test.com

 

This is all self-explanatory. Name, address and email. The | after sharons last name is the empty company field.

  

||||||||||||||5E3E841F808768EA1E68EB5800CD0A48|||||||||||||XXXX0002|American Express|||||||||||||||||422E3EF8AA729B89FC102825EA9FEE6DCCB701D861B4248CF24C12600AB1EAF04371257B7163091AFCD883D57C3F9B83424717C7317BCFD90392CE33E87BFCE2

 

Here is md5 hash, x_account_number, and x_card_type, followed by the sha512 hash value.

 

Of these you need the values of x_trans_id,  x_respone_code, x_auth_code, x_sha_512, and x_method (maybe on x_method, you can probably pass this in your call too).

 

The remaining values to calculate your string for verification are in your original request. That cuts you down to 4 to 5 values to extract, which is better than 30. You don’t care what all those other pipe bars are because they’re all empty. You could trade them out for one another with no effect.

 

@smorrow123 You might take a look at this near the bottom. Again, I've never used LWP so I can't add a lot of input here.

 

http://forums.devshed.com/perl-programming-6/reading-post-data-lwp-response-object-536104.html


If no help there you could consider trying simple CGI as a work araound.

 

If you already have or can add this near the top of your script?

 

 use CGI;
 $cgi = new CGI;

Then do a quick check to see if CGI will give you the name/value pairs.

 

if( $response->is_success )
{
       #$apiresponse = $response->content;  
       #foreach $pair(@responses)
       #{
              # The next statement splits based on the pipe character: |
              # not sure what this accomplishes since it isn't referenced by name or val
       #       ($name,$val) = split(/\Q|/,$pair);   
       #}
       # split the words using the pipe character (|) as the delimiter.
       #@words = split /\Q|/, $apiresponse;
       #$response_code = $words[0];

###############################################
use CGI;        ### Could also place this
$cgi = new CGI; ###  here if not elsewhere 

foreach $name ($cgi->param) {
   $value=$cgi->param($name);
   $response{$name}=$value; 
}
while (($name,$value) = each(%response)) {
  $debug.="$name = $value ";
}
print qq~Content-type: text/html\n\n
<html>
$response{x_response_code}<br>
$response{x_amount}<br>
$response{x_SHA2_Hash}<br>
<br>Full Set = $debug<br>
</html>~;
exit;
##############################################

      #additional processing; check response code, hashing, checking hash, etc. }

}

@airman81

Thanks! I've been considering the cgi alternative, but the script was already doing cgi (the program does more than just obtain authorizations) and I've never done two "new cgi's" in the same script. As I said in a recent email, I would imagine that if I call it by another name (I use $q as a standard) it should be ok. The only problem is actually generating the "post". I would normally do it via forms and since this currently uses LWP for posting, I'd have to figure out how to code it to post with a regular cgi post rather than LWP. (Don't know if that makes sense).

I will read the LWP reference on devshed - I also have an email into our server host to see if he can get me some LWP documentation.

I'm on hold with this at least until tomorrow due to other obligations. Please know that I am reading all the suggestions and appreciate the amount of time and effort you and the others have put into helping me with this problem.  I've been ready to give up multiple times, but you guys manage to draw me back in to keep trying! ;-) If I finally get this working - no matter how - I will post to let everyone know what the resolution was.

@smorrow123Just noticed I should have used a different hash name for testing to avoid any issues with how "response" is already being used. Changed response to responseT.

 

###############################################
use CGI;        ### Could also place this
$cgi = new CGI; ###  here if not elsewhere 

foreach $name ($cgi->param) {
   $value=$cgi->param($name);
   $responseT{$name}=$value; 
}
while (($name,$value) = each(%responseT)) {
  $debug.="$name = $value ";
}
print qq~Content-type: text/html\n\n
<html>
$responseT{x_response_code}<br>
$responseT{x_amount}<br>
$responseT{x_SHA2_Hash}<br>
<br>Full Set = $debug<br>
</html>~;
exit;
##############################################

@airman81

I checked out the LWP reference in the link from DevShed, and it seems like I SHOULD be able to get name/value pairs back, so I'm going to try their sample code. That isn't what I was seeing when I displayed the response (I only saw values). One post there said LWP doesn't return name/value pairs - then they proceeded to provide code that would split out the name/value pairs. Made no sense. But I'm still going to try it.

THEN, if that doesn't work, I'm going to try the cgi route. However, using your example below, is this IN ADDITION TO using LWP, or INSTEAD OF? In other words, can I access the cgi params if I've used LWP - or do I need a different kind of POST statement to use CGI? And if so, all of my cgi posts have been via forms - and since there isn't a form per-say in this case, how do I generate the CGI POST statement to use THIS method?

Hope this is making sense. In the meantime, I'm going to try the LWP suggestion from DevShed and see what happens.

It looks like you are using AIM, take a look at https://www.authorize.net/content/dam/authorize/documents/AIM_guide.pdf. Page 52 lists the fileds in the response. Based on some old code of mine I believe they added more fields later, but I don't think they are important in this context.

 

With AIM you submit data in name value pairs - x-login=xxxx&x_tran-key=zzz etc.

The response is not in name value pairs, it is one delimited string. So there are no names to extract via perl or any other language.

 

The response needs to be first split into an array, then addressed by the array index.

 

my @responses  = split( /\Q|/, $response->content );

This line is splitting the response string into an array,  so $responses[0] is the Response Code, $response[2] is the Response Reason Code, etc. 

 

If you want to address the fields by name, you must assign the names. For example 

$ResponseCode = $responses[0];
or
$respone_hash = (ResponseCode => $responses[0], ResponseReasonCode => $responses[2]);

 

This is quite easy as long as you remember that perl arrays are 0 based, so the first field in the response is $responses[0].

@jgoebel

Thanks!!

The old code I inherited (as you showed) does make it sound like I'm using AIM. Someone else thought SIM. Who knows WHAT it's using!! All I know is that it isn't using an API.

 

SO, yes, I am only getting values returned and separated by pipe symbols - no names - and I understand what you are saying about assigning names. I can do that. But it says that the values can be returned in a different order so it was best to access by field-names.  That's where I started on this crazy journey and where I still am.

 

SO, assuming that the order on the returned values are the same as the current version of the documentation - can you tell me where I can get a list of the returned values IN THE ORDER THEY ARE RETURNED? Another developer helped me figure out most, but there are some questionable ones. The HMAC SHA-512 Hash section in the SIM Developer Guide that I was sent says that the response fields will be incorporated into the hash calculation in the sequence shown in the table on page 73. But that is NOT the order they are returned in the response. Where can I get information on THAT order?

 

I tried just hashing with the first 30 fields that were returned to me, hoping they were being returned in the same order as the hash response, but that didn't work either - so I can only assume they are in a different order for the hashing than they are actually returned. WHY? Who knows!

 

Please - if you can get me information on the returned data order - I think I will actually be able to get this implemented!!!  

 

 

 

@smorrow123

My post above has the order. If you’re using AIM I would check up on how your company is maintaining PCI compliance. The compliance burden for that method is enormous.

But the response you printed out should be the order they are in until the next security update. Read all of
my most recent posts for all of those details and how to test this.

I have your printed response completely decoded above, with the exception of the number starting with 100, which I think may be the customer id but which could be anything.

You only need 5 values or less from that api response. The other 25 are in your request and you can pull them from there.

But seriously, look into your PCI procedures. If you don’t have access to people who can do this type of thing for you that is a sign that you probably aren’t implementing all that the payment card industry holds you to, which is about 1,000x the cost of fixing this code. Sounds like you’ve had this application for a bit, and a lot has changed in regards to PCI. Not trying to get in your business, just trying to help you dodge some big trouble down road.