#!/usr/bin/perl ## This Perl CGI is a sample KRPS external CGI ### Copyright © 2004 Kagi. All rights reserved. # 1442A Walnut Street PMB #392, Berkeley, CA 94709, USA. ### Permission is granted to use this specification and code for development and # deployment of a Kagi Remote Post CGI free of charge as long as Kagi sells # your products. ## No warranty is made as to the suitability of code herein # or even this code is bug or error free. ## BECAUSE THE CODE IS LICENSED FREE OF CHARGE, THERE IS NO # WARRANTY FOR THE CODE, TO THE EXTENT PERMITTED BY # APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING # THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDING THE # CODE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE. YOU ASSUME THE ENTIRE RISK AS TO THE # QUALITY AND PERFORMANCE OF THE CODE. SHOULD THE CODE # PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL-NECESSARY # SERVICING, REPAIR OR CORRECTION. package KagiRemoteCGIPostReceiver; use strict; use Exporter; use English; use CGI; use Digest::MD5; # end of line and return for CGI and KRPS my ($cr,$lf) = ("\015", "\012"); my ($crlf) = ("$cr$lf"); &main; 1; exit; sub main(){ # # CGI programs must print their own HTTP # response headers # printf ("Content-type: text/text%s%s",$crlf,$crlf); my $cgi = new CGI; #read cgi input parameters # does it start correctly? if( !defined($cgi) ){ printf( "kagiRemotePostStatus=BAD, message=Could not get CGI started.%s%s",$crlf,$crlf ); return(0); } # get the message and the digest for comparison my $sharedSecret = "myTestPassword", my $digest = md5Password($sharedSecret,$cgi); if( !defined($digest) ){ # status set in call, just return return(0); } # Specifically get the password from the input parameters my $passwd = $cgi->param('ACG:Password'); #does the password match what I think it should be if( $digest ne $passwd ){ printf( "kagiRemotePostStatus=BAD, message=Password did not match calculated version. %s%s”, $crlf,$crlf ); return(0); } # OK we passed the password test, now do something useful my ($userName,$regNumber) = myalgo($cgi->param('ACG:PurchaserEmail); printf( "kagiRemotePostStatus=GOOD, message=Authenticated call from Kagi.%s%s",$crlf,$crlf ); printf( “username=%s,regNumber=%s%s%s”. $username,$regNumber,$crlf,$crlf); return(1); } sub md5Password($$){ # Calculate the KRPS encrypted password message and return lower case version # # The result of this routine should match what is passed by Kagi if the MD5 # encryption hash was specified during setup. # Reference Appendix C for details on the implementation # my($sharedSecrete, $cgi) = @ARG; # build the password as MD5 Hash my $md5 = Digest::MD5->new(); if( !defined($md5) ){ printf("kagiRemotePostStatus=BAD, message=MD5 could not be initialized. %s%s", $crlf,$crlf) } # lowercase the data to be consistent, no spaces # this is step 1 and step 2 in Appendix C $md5->add( lc( $ sharedSecrete . $cgi->param("ACG:TransactionID") . $cgi->param("ACG:ProductName") . $cgi->param("ACG:UnitPayment") . $cgi->param("ACG:DateProcessed") . $cgi->param("ACG:QuantityOrdered") . $cgi->param("ACG:LicenseType") ) ); #we lowercase the password, this is step 3 and step 4 in Appendix C my $newHash = lc( $md5->hexdigest ); # Perl adds spaces to output only, other libraries might add more my ($cleanHash,@stringArray,$character) = ("",undef,undef); # this is more of step 4 of Appendix C, Perl only, yours will vary. @stringArray = split(//,$newHash); # this is more of step 4 of Appendix C, Perl only, yours will vary. foreach $character (@stringArray){ # only accept hex characters if( $character =~ /[a-f0-9]/ ){ $cleanHash.= $character; } } return( $cleanHash); } 1; #### KRPS Test Page

ACG test page

The seed parameters are comma separated. Internal commas come escaped with a comma. The maximum length of the value for a given seed is 256 bytes

ACG:Flags


ACG:UserName


ACG:Password


ACG:CustomerSeed


ACG:OEMSeed


ACG:DebugFlag

Value 0 or 1

ACG:InputVersion

Value 0200

ACG:Request

Value is 'Generate' and 'Information'. See specification for meanings

ACG:CardName


ACG:CardName-8bit


ACG:PurchaserName


ACG:PurchaserName-8bit


ACG:PurchaserEmail


ACG:SQNM

Value 0000 to 9999

ACG:QuantityOrdered


ACG:LicenseType

See specification for meanings

ACG:UnitPayment


ACG:TransactionID


ACG:Postal-8bit


ACG:Postal


ACG:ProductName-8bit


ACG:ProductName


ACG:UserPurchaseDate


ACG:DateProcessed


ACG:TimeStamp


Submit