How to read file from smart card using java

Krunal Indrodiya picture Krunal Indrodiya · Apr 18, 2016 · Viewed 16k times · Source

I am new in smart card technology. I want to read file from smart card. I am using javax.smartcardio to read value. I developed some code to connecting system to card (That's work fine). I have card ATR and other detail also. But not getting proper help for APDU command for communication with Smart card. Stuck in APDU command.

Answer

Abraham picture Abraham · Apr 18, 2016

First of all:

"Not" all Java cards have MF, DF and EF inside! These words stands for Master File, Dedicated File and Elementary File in order. They are components of an ISO7816-defined system file for smart card (refer to part 4 of ISO7816), so your card maybe or maybe not have this file system.

Typical java cards have a storage that you can install your applets in it (after a successful authentication for sure) and register name of your applet (we call it AID, that stands for Applet IDentifier and it is a 5 to 16 byte hexadecimal sequence) in the card's registry table (a table of loaded/installed applets and packages that contain life-cycles and priviledges too - read Global Platform Card Spec).

And then:

Let assume that you have a smart card inserted in your card reader that is connected to your computer. You have different options to have a communication between your computer and the card.

1-You can use available tools such as your reader's tool (almost all readers have one tool), PyAPDUTool, etc.

2-You can use Javax.smartcardio library for writing your Java program to communicate with smart cards:

import java.util.List;
import java.util.Scanner;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;
import javax.xml.bind.DatatypeConverter;

public class TestPCSC {

    public static void main(String[] args) throws CardException {

        TerminalFactory tf = TerminalFactory.getDefault();
        List< CardTerminal> terminals = tf.terminals().list();
        System.out.println("Available Readers:");
        System.out.println(terminals + "\n");

        Scanner scanner = new Scanner(System.in);
        System.out.print("Which reader do you want to send your commands to? (0 or 1 or ...): ");
        String input = scanner.nextLine();
        int readerNum = Integer.parseInt(input);
        CardTerminal cardTerminal = (CardTerminal) terminals.get(readerNum);
        Card connection = cardTerminal.connect("DIRECT");
        CardChannel cardChannel = connection.getBasicChannel();

        System.out.println("Write your commands in Hex form, without '0x' or Space charaters.");
        System.out.println("\n---------------------------------------------------");
        System.out.println("Pseudo-APDU Mode:");
        System.out.println("---------------------------------------------------");
        while (true) {
            System.out.println("Pseudo-APDU command: (Enter 0 to send APDU command)");
            String cmd = scanner.nextLine();
            if (cmd.equals("0")) {
                break;
            }
            System.out.println("Command  : " + cmd);
            byte[] cmdArray = hexStringToByteArray(cmd);
            byte[] resp = connection.transmitControlCommand(CONTROL_CODE(), cmdArray);
            String hex = DatatypeConverter.printHexBinary(resp);
            System.out.println("Response : " + hex + "\n");
        }

        System.out.println("\n---------------------------------------------------");
        System.out.println("APDU Mode:");
        System.out.println("---------------------------------------------------");

        while (true) {
            System.out.println("APDU command: (Enter 0 to exit)");
            String cmd = scanner.nextLine();
            if (cmd.equals("0")) {
                break;
            }
            System.out.println("Command  : " + cmd);
            byte[] cmdArray = hexStringToByteArray(cmd);
            ResponseAPDU resp = cardChannel.transmit(new CommandAPDU(cmdArray));
            byte[] respB = resp.getBytes();
            String hex = DatatypeConverter.printHexBinary(respB);
            System.out.println("Response : " + hex + "\n");
        }

        connection.disconnect(true);

    }

    public static int CONTROL_CODE() {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.indexOf("windows") > -1) {
            /* Value used by both MS' CCID driver and SpringCard's CCID driver */
            return (0x31 << 16 | 3500 << 2);
        } else {
            /* Value used by PCSC-Lite */
            return 0x42000000 + 1;
        }

    }

    public static byte[] hexStringToByteArray(String s) {
        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;
    }

}

3- You can use PySCard library to write your Python program to communicate with smart cards:

#Importing required modules.
import sys
import time
#--- You may need to change the following "line" based on your pyScard library installation path
sys.path.append("D:\\PythonX\\Lib\\site-packages")
from smartcard.scard import *
import smartcard.util
from smartcard.System import readers


#---This is the list of commands that we want to send device
cmds =[[,0xFF,0x69,0x44,0x42,0x05,0x68,0x92,0x00,0x04,0x00],]


#--- Let's to make a connection to the card reader
r=readers()
print "Available Readers :",r
print
target_reader = input("--- Select Reader (0, 1 , ...): ")
print

while(True):
    try:
        print "Using :",r[target_reader]
        reader = r[target_reader]
        connection=reader.createConnection()
        connection.connect()
        break
    except:
        print "--- Exception occured! (Wrong reader or No card present)"
        ans = raw_input("--- Try again? (0:Exit/1:Again/2:Change Reader)")
        if int(ans)==0:
            exit()
        elif int(ans)==2:
            target_reader = input("Select Reader (0, 1 , ...): ")

#--- An struct for APDU responses consist of Data, SW1 and SW2
class stru:
    def __init__(self):
        self.data = list()
        self.sw1 = 0
        self.sw2 = 0

resp = stru()

def send(cmds):
    for cmd in cmds:

        #--- Following 5 line added to have a good format of command in the output.
        temp = stru() ;
        temp.data[:]=cmd[:]
        temp.sw1=12
        temp.sw2=32
        modifyFormat(temp)
        print "req: ", temp.data

        resp.data,resp.sw1,resp.sw2 = connection.transmit(cmd)
        modifyFormat(resp)
        printResponse(resp)

def modifyFormat(resp):
    resp.sw1=hex(resp.sw1)
    resp.sw2=hex(resp.sw2)   
    if (len(resp.sw2)<4):
        resp.sw2=resp.sw2[0:2]+'0'+resp.sw2[2]
    for i in range(0,len(resp.data)):
        resp.data[i]=hex(resp.data[i])
        if (len(resp.data[i])<4):
            resp.data[i]=resp.data[i][0:2]+'0'+resp.data[i][2]

def printResponse(resp):
    print "res: ", resp.data,resp.sw1,resp.sw2


send(cmds)
connection.disconnect()

4- You can use WinSCard library to write your program in C++/.Net (Not sure) to communicate with smart cards.

Above programs are sample programs to send APDU commands to smart cards. But the commands themselve are depend on your card and the applets that [your] are installed on it.

For example let assume that you are write an applet with AID = 01 02 03 04 05 00 that returns 11 22 33 44 55 when it receive 00 00 00 00 00 as APDU command. What you need to do to receive this response (i.e. 11 22 33 44 55) is as below:

  1. Send a SELECT APDU command with you applet's AID in its data field.
  2. Sending 00 00 00 00 00 to your applet.
  3. Your applet response to the above command with expected answer.

If your card have been implemented ISO7816 system file, you need ID of files to select them. but the commands themselves are defined in ISO7816-P4.

Even if your cards doesn't implemented ISO7816 system file, you can write an applet to act like a ISO7816-P system file implemented smart card (Not easy anyway).

As the ID of MF is 3F00 always, trying to select this file, will show you if your card implemented the system-file or not.

Typically when your card powers on, a mandatory entity in the card named Card Manager, receive your APDU commands. By using SELECT APDU command, you request the card manager to send next incoming commands to the selected APPLET