Introduction
This article is about writing Java Smart card based applications. This tutorial will help beginners to understand the concepts and communication between a Java Smart Card and a host application. I have seen beginners of Java Smart Card technology ask simple questions, so I decided to provide them with a complete example to get them started.
In this article/tutorial I will explain a sample application, a Calculator which will perform four basic operations of calculations, i.e,, + ,-, * and /.
Background
In order to understand this tutorial you must know J2SE and have should have basic understanding of (Java) Smart Cards. To get to what java card is please visit the Oracle official site here.
Moreover you might need to have the basics understanding of following standards:
- ISO 7816-3
- ISO 7816-4
- Global Platform 2.1/2.2
Assumptions
I am assuming that you have smart card and a smart card reader and you are able to load and install the .cap file provided with this tutorial/article.
Tools Used
- Netbeans,
- Java smart card
- Dell Keyboard reader
Definitions
What is Smart Card Applet?
An application which resides on the smart card is called Smart Card Applet. It is written on the computer and then been install on the smart card.
What is Host Applications?
It is the application which resides on the computer or and interacts with the smart card via APDUs. This application can be written in any programming language.
What is an APDU ?
APDU Stands for Application Programming Data Unit. It is the communication medium between the applet and the host application. All the communication are done between the host application and applet via APDUs.
APDU is of two types one is command APDU which is send by the Host Application towards the applet and the second is response APDU wich is send by the as a response of the command APDU back to Host Application.
What is APDU Structure ?
An APDU consists of following fields:
- CLA: it is the class of APDU
- INS: Instruction what host application wants to do
- P1 & P2: Switching parameters
- LC: Length of the data
- Data: Actual data being sent to card
- LE: Length of data expecting from card
The sequence of above fields should be:
CLA INS P1 P2 LC Data LE.
Using the code
Java card application is a sort of Client Server application in which smart card always remains idle and respond to the commands that. Host application sends to it. There is always a response APDU of a command APDU.
In any Smart card applications we need to detect the reader(s) attached with the computer and then have to make connection with that reader and will connect with the card inside that reader.
In the Calculator application I am using a combo-box to display all the available readers and a Button called’ Refresh’ which when clicked populates the combo-box with the attached terminals/readers. After then you have to choose a terminal and click on the ‘Connect’ button to make connection with the smart card.
I am using SmartCardIO API which comes officially with the JDK 1.6+ that means you don't need to download it, just import it and use it. In calculator application following classes of SmartCardIO are used:
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;
To start communication with the card we need to get the reader/terminal first. To do so Java has provided a class TerminalFactory
and this class is used to get all the terminals attached with the computer.
public List<CardTerminal> getTerminals() throws Exception{
factory = TerminalFactory.getDefault();
terminals = factory.terminals().list();
return terminals;
}
Above function returns a List
of readers that we can display in the combo-box.
After selecting a terminal from the combo-box the user have to click on the Connect
button. If there is a card present and and its ATR is working fine then a text Connected will be displayed on the right-bottom side of the frame else corresponding error message will be displayed.
In order to connect with the smart card, we are using Connect()
function of the CardTerminal
class. To make connection via T=0 you will need to use Connect ("T=0")
and for T=1
you have to use Connect("T=1")
. But if you are not sure you can use *
and the SmartCardIO
will detect the communication protocol automatically.
Following method is performing the connect operation:
protected void connectToCard(CardTerminal terninalSource) throws CardException {
terminal = terninalSource;
card = terminal.connect("*");
}
After successful connection with the card you need to input digits in the input files and press the calculation operation buttons.
I am going to explain the (+)
operation here the rest of all are the same.
private void add_buttonActionPerformed(java.awt.event.ActionEvent evt) {
String command = "00A404000E63616C63756C61746F722E61707000";
byte[] apdu = JavaSmartcard.hexStringToByteArray(command);
if (!selectApplet(apdu))
{
return;
}
byte[] data_LC;
try
{
data_LC = getLCData(this.digit1_TextField.getText(), this.digit2_TextField.getText());
}
catch (Exception ex)
{
JOptionPane.showMessageDialog(this, "Only digits are allowed to input in the fields\n"+
ex.getMessage(), "Type Error", JOptionPane.ERROR_MESSAGE);
return;
}
command = "A000000002";
String LC_Hex = JavaSmartcard.byteArrayToHexString(data_LC);
command = command.concat(LC_Hex);
apdu = JavaSmartcard.hexStringToByteArray(command);
System.out.println(""+ JavaSmartcard.htos(apdu));
try
{
javaCard.sendApdu(apdu);
byte[] data = javaCard.getData();
this.status_Label.setText(""+Integer.toHexString(javaCard.getStatusWords()).toUpperCase());
this.result_Label.setText(new BigInteger(data)+"");
}
catch (CardException | IllegalArgumentException ex)
{
JOptionPane.showMessageDialog(this, "Error while tried to send command APDU\n"+
ex.getMessage()+"", "APDU sending fail", JOptionPane.ERROR_MESSAGE);
}
}
In the above function I am first selection the applet and on the successful selection I am preparing the APDU which will instruct the applet what to do and what data it has.
command = "00A404000E63616C63756C61746F722E61707000";
Above is the select applet APDU as we need to select our calculator applet first in order to do calculation otherwise the default applet might not accept your subsequent command APDUs.
byte[] apdu = JavaSmartcard.hexStringToByteArray(command);
After preparing the APDU I am gonna convert it into the byte array to transmit to the card. hexStringToByteArray
is a utility function used to convert a hex string into byte array.
command = "A000000002";
Above APDU A000000002
is the APDU which will tell the applet that you have to add given two digits. I am gonna to explain its fields here below:
- CLA: AO
- INS: 00
- P1 & P2: 00, 00
- LC : 02
Data part is calculated as below:
private byte[] getLCData(String byte1Str, String byte2Str) throws Exception
{
byte[] data_LC = new byte[2];
byte byte1 = Byte.parseByte(byte1Str );
byte byte2 = Byte.parseByte(byte2Str);
data_LC[0] = byte1;
data_LC[1] = byte2;
return data_LC;
}
I am converting the inputs of both the TextFields into the byte and then copying that bytes into a byte array to transmit to the card.
The final APDU will looks like below. Let user enters 5 and 5 and input.
A0 00 00 00 02 05 05
When smart card gets that APDU it will interprets it and find the INS
field to know what the Host Application want to do and then it will gets the data (digits) from the Data part and will add and returns it to the Host Application in the form of response APDU.
When the response APDU received, we can determine the STATUS WORD
to get to know what happened during the calculation. If card returns a STATUS WORD
of 0x9000
that will means that everything was went fine and else there might be error or do perform further actions to get the actual response APDU.
public int getStatusWords() {
return rAPDU.getSW();
}
The above function is used to get the STATUS WROD return by the card and by using the below function I am getting the Data part.
public byte[] getData() {
if (rAPDU!=null) {
return rAPDU.getData();
}
else {
return null;
}
}
Rest of the code is simple and self-explanatory. I will try to add another tutorial on writing the calculator applet which I am attaching with this article.
Further readings can be done on blog: https://gosiebel.blogspot.com/
Points of Interest
In this example no special or third party dll, library or API is being used. Everything is done by using the Java provided SDK.
Limitations
As smart cards are limited resource and computations enabled devices so this calculator worked with the rang of 1-127 inclusive. The reason is that I am converting the input into byte and a byte can contains 127 signed values.