package br.com.paysmart;
import java.util.List;
import javax.smartcardio.*;
import java.util.*;
import java.text.SimpleDateFormat;
public class paySmartTest {
private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
public static String toHexString(byte[] buf)
{
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i)
{
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
public static byte[] hexStringToByteArray(String s) {
s = s.replaceAll("\\W+","");
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static byte[] concat( byte[]...arrays )
{
// Determine the length of the result array
int totalLength = 0;
for (int i = 0; i < arrays.length; i++)
{
totalLength += arrays[i].length;
}
// create the result array
byte[] result = new byte[totalLength];
// copy the source arrays into the result array
int currentIndex = 0;
for (int i = 0; i < arrays.length; i++)
{
System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
currentIndex += arrays[i].length;
}
return result;
}
public static byte[] getCurrentDateAsByteArray( String sFormat )
{
SimpleDateFormat sdfDateFormatted = new SimpleDateFormat( sFormat );
Date now = new Date();
String sDate = sdfDateFormatted.format(now);
return hexStringToByteArray ( sDate );
}
public static byte[] extractTLVFromBuffer( int iTag, byte[] buf )
{
//@todo: check size, check 77, etc
int i;
byte[] bufExtracted = buf;
TLV tlvBuf = new TLV( buf, 0 );
if ( iTag <= 0xFF )
{
//System.out.println("iTag= "+ iTag + " tlvBuf.type= " + tlvBuf.type + " buf= "+ toHexString( buf ) );
if ( tlvBuf.type == iTag )
{
//System.out.println("Found tag = "+ iTag + " " + toHexString( tlvBuf.getValue() ) );
return tlvBuf.getValue();
}
else
{
/*
TLV tlvNext = tlvBuf.next;
if ( tlvNext != null )
System.out.println( "next = " + toHexString( tlvNext.getDERData() ) );
else
System.out.println( "next = NULL" );
while ( tlvNext != null )
{
System.out.println("traversing next, looking for "+ iTag );
return extractTLVFromBuffer( iTag, tlvBuf.next.getDERData() );
}
*/
return extractTLVFromBuffer( iTag, tlvBuf.getValue() );
}
}
// lookup for iTag
else
{
//@todo double tags
/*
byte bTag1 = (byte) ( iTag & 0x00FF );
byte bTag2 = (byte) (( iTag & 0xFF00 ) >> 2 );
*/
}
return buf;
}
public static String TLVListToString( byte[] bufTLV )
{
String sList = "";
int iOffset = 0;
int iTLV= 0;
System.out.println("[TLVListToString]");
while ( iOffset < bufTLV.length )
{
TLV tlvCurrent = new TLV( bufTLV, iOffset );
iTLV++;
System.out.println(" TLV[" + iTLV + "].tag = " + tlvCurrent.type );
System.out.println(" TLV[" + iTLV + "].size = " + tlvCurrent.length );
System.out.println(" TLV[" + iTLV + "].value = " + toHexString(tlvCurrent.getValue() ) );
iOffset+= tlvCurrent.getDERData().length; //System.out.println(" TLV["+iTLV+"].DER = " + toHexString(tlvCurrent.getDERData() ) );
System.out.println( iOffset );
System.out.println();
sList += toHexString( tlvCurrent.getValue() ) + "\n";
}
return sList;
}
public static String printAPDU( ResponseAPDU apdu )
{
String sAux = apdu.toString() + " data=" + toHexString( apdu.getData() );
return sAux;
}
public static void main( String[] args )
{
try
{
boolean boolEverythingAsTLV = true;
int i;
Random RandomNumberGenerator = new Random();
System.out.println("_______________________________________________________");
System.out.println("paySmart EMV Minimalist Terminal v1.0");
System.out.println("www.paySmart.com.br");
System.out.println("_______________________________________________________");
System.out.println("");
for (i=0; i<args.length; i++)
System.out.println( "Param " + i + ": "+args[i] );
System.out.println();
if ( args.length < 2 )
{
System.out.println("");
System.out.println("usage: paySmartTest #reader AID [Host Port]");
System.out.println(" example1> paySmartTest 0 A0000005551010");
System.out.println(" example2> paySmartTest 1 A000000555E010");
System.out.println(" example3> paySmartTest 1 A0000000041010");
System.out.println(" example4> paySmartTest 1 A0000000041010 10.2.1.1 887");
System.out.println("");
System.out.println("#ERROR# Invalid Params");
System.exit(1); // throw new Exception( "Invalid params" );
}
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = null;
// Display the list of terminals
try
{
terminals = factory.terminals().list();
System.out.println("Smart card readers: ");
System.out.println( terminals );
}
catch(Exception e)
{
System.out.println("#ERROR# No readers found");
System.exit(2); //throw new Exception( "No readers found" );
}
int iReader=0;
// Use the first terminal
try
{
iReader = Integer.parseInt( args[0] );
}
catch( Exception e )
{
System.out.println("#ERROR# Invalid reader index format '"+args[0]+"'" );
System.exit(3);// throw new Exception( "Invalid reader index '"+args[0]+"'" );
}
if ( iReader >= terminals.size() )
{
System.out.println("#ERROR# Invalid reader index '"+args[0]+"'. Last valid index is " + (terminals.size()-1) );
System.exit(3); // throw new Exception( "Invalid reader index '"+iReader+"'. Last valid index is "+(terminals.size()-1) );
}
CardTerminal terminal = terminals.get( iReader );
// Connect with the card
Card card = null;
CardChannel channel = null;
try
{
card = terminal.connect("*");
System.out.println(" card: " + card );
channel = card.getBasicChannel();
System.out.println(" ATR=" + toHexString( card.getATR().getBytes() ) );
System.out.println( "" );
}
catch( Exception e )
{
System.out.println("#ERROR# Cannot connect to card");
System.exit(4); // throw new Exception( "Cannot connect to card" );
}
// Send Select Application command
//String sAID = "A000000555E010"; // MAIS ACEITO
//String sAID = "A0 00 00 05 55 10 10";
String sAID = args[1];
byte[] bufAID = hexStringToByteArray( sAID );
System.out.println( "SELECT (AID = " + toHexString( bufAID ) + ")" );
ResponseAPDU answer = channel.transmit(new CommandAPDU(0x00, 0xA4, 0x04, 0x00, bufAID ));
System.out.println( " FCI = "+ toHexString( answer.getData() ) );
System.out.println( "" );
if ( answer.getSW() != 0x9000 )
{
System.out.println("#ERROR# Invalid AID '"+sAID+"'" );
System.exit(5); //throw new Exception( "Invalid AID '"+sAID+"'" );
}
//Open MEL Application
byte[] bufMEL = {(byte) 0x83, 0x00 };
System.out.println( "MEL (MEL = " + toHexString( bufMEL ) + ")" );
answer = channel.transmit( new CommandAPDU(0xBE, 0x12, 0x00, 0x00, bufMEL, 0x00 ) );
System.out.println( " " + printAPDU( answer ) );
//Send GPO
byte[] bufPDOL = {(byte) 0x83, 0x00 };
System.out.println( "GPO (PDOL = " + toHexString( bufPDOL ) + ")" );
answer = channel.transmit( new CommandAPDU(0x80, 0xA8, 0x00, 0x00, bufPDOL ) );
System.out.println( " " + printAPDU( answer ) );
byte[] bufAIP = extractTLVFromBuffer( 0x82, answer.getData() );
//byte[] bufAFL = extractTLVFromBuffer( 0x94, answer.getData() );
System.out.println( " AIP = " + toHexString( bufAIP ) );
//System.out.println( " AFL = " + toHexString( bufAIP ) );
System.out.println( "" );
//Send ReadRecord 2
System.out.println( "READ RECORD (SFI=1, REC=2)" );
answer = channel.transmit( new CommandAPDU(0x00, 0xB2, 0x02, 0x0C, 0x3D ) ); //fixed :-( @todo GetResponse
System.out.println( printAPDU( answer ) );
byte[] bufExpiryDate = {0x17, 0x01, 0x01}; //Arrays.copyOfRange( answer.getData(), 11, 03 );
System.out.println( " ExpiryDate= " + toHexString( bufExpiryDate ) );
byte [] bufSlice = Arrays.copyOfRange( answer.getData(), 14, answer.getData().length-14 );
byte[] bufPAN = extractTLVFromBuffer( 0x5A, bufSlice );
System.out.println( " PAN = " + toHexString(bufPAN) );
byte[] bufPANSequence = { 0x00 }; // @todo extractTLVFromBuffer( 0x5F34, ... );
System.out.println( " PANSequence = " + toHexString(bufPANSequence) );
System.out.println( "" );
//Send ReadRecord 3
System.out.println( "READ RECORD (SFI=1, REC=3)" );
answer = channel.transmit( new CommandAPDU(0x00, 0xB2, 0x03, 0x0C, 0x43 ) ); //fixed :-( @todo GetResponse
System.out.println( printAPDU( answer ) );
byte[] bufSlice2 = Arrays.copyOfRange( answer.getData(), 2, answer.getData().length-2 );
byte[] bufTrack2 = extractTLVFromBuffer( 0x57, bufSlice2 );
System.out.println( " Track2= " + toHexString( bufTrack2 ) );
System.out.println( );
byte[] bufAmountAuthorized = hexStringToByteArray ("00 00 00 00 00 00"); // 9F02 06 ;$0.00 (assuming currency exponent = 2)
byte[] bufAmountOther = hexStringToByteArray ("00 00 00 00 00 00"); // 9F03 06 ;$0.00 (assuming currency exponent = 2)
byte[] bufTermCountryCode = hexStringToByteArray ("00 76"); // 9F1A 02 ;Brazil
byte[] bufTVR = hexStringToByteArray ("00 00 00 08 00"); // 95 05 ;Merchant forced transaction to go online
byte[] bufTxCurrencyCode = hexStringToByteArray ("09 86"); // 5F2A 02 ;Brazilian Reals (BRL)
byte[] bufTxDate = getCurrentDateAsByteArray("yyMMdd"); // 9A 03 ;04.SET.2014
byte[] bufTxType = hexStringToByteArray ("00"); // 9C 01 ;Goods & services
byte[] bufUN = new byte[4]; RandomNumberGenerator.nextBytes(bufUN); // 9F37 04 ;Unpredictable Number
System.out.println( " [Transaction Parameters]");
System.out.println( " AmountAuthorized= "+ toHexString( bufAmountAuthorized ) );
System.out.println( " AmountOther= "+ toHexString( bufAmountOther ) );
System.out.println( " TCC= "+ toHexString( bufTermCountryCode ) );
System.out.println( " TVR= "+ toHexString( bufTVR ) );
System.out.println( " TxDate= "+ toHexString( bufTxDate ) );
System.out.println( " TxType= "+ toHexString( bufTxType ) );
System.out.println( " UN= "+ toHexString( bufUN ) );
System.out.println( "");
byte[] bufTxData = concat ( bufAmountAuthorized, bufAmountOther, bufTermCountryCode, bufTVR, bufTxCurrencyCode, bufTxDate, bufTxType, bufUN );
System.out.println( "GenerateAC (TxData= " + toHexString( bufTxData ) + ")" );
answer = channel.transmit( new CommandAPDU( 0x80, 0xAE, 0x40, 0x00, bufTxData ) );
System.out.println( printAPDU( answer ) );
System.out.println( "" );
// get 9F27, 9F36, 9F10, 9F26
byte[] bufResponse = Arrays.copyOfRange( answer.getData(), 2, answer.getData().length );
byte[] bufBit55 = concat( hexStringToByteArray ("82 02"), bufAIP,
hexStringToByteArray ("84 07"), bufAID,
hexStringToByteArray ("9F1A 02"), bufTermCountryCode,
hexStringToByteArray ("95 05"), bufTVR,
hexStringToByteArray ("9C 01"), bufTxType,
hexStringToByteArray ("9F37 04"), bufUN,
bufResponse );
if ( boolEverythingAsTLV )
{
bufBit55 = concat(
hexStringToByteArray ("5F34 01"), bufPANSequence,
hexStringToByteArray ( "5A 08"), bufPAN,
//hexStringToByteArray ("5F34 01"), bufPANSequence,
hexStringToByteArray ("9F02 06"), bufAmountAuthorized,
hexStringToByteArray ("9F03 06"), bufAmountOther,
hexStringToByteArray ("5F2A 02"), bufTxCurrencyCode,
hexStringToByteArray ( "9A 03"), bufTxDate,
bufBit55 );
}
System.out.println( "Bit55 = ");
System.out.println( toHexString( bufBit55 ) );
System.out.println( );
// Disconnect the card
card.disconnect(false);
/*
// Send to host
if ( args.length >= 4 )
{
String sHost = args[2];
int iPort = Integer.parseInt( args[3] );
SendISOPacket( sHost,
iPort,
bufPAN,
bufAmountAuthorized,
bufAmountOther,
bufTxCurrencyCode,
bufTxDate,
bufExpiryDate,
bufTermCountryCode,
bufTrack2,
bufBit55
);
}
*/
System.exit(0);
} catch(Exception e)
{
System.out.println( "#ERROR#: " + e.toString() );
System.exit(6);
}
}
}