You've al seen them or used them, the smartcards. Best know cards are the payment cards (Visa, mastercard, Sis card, belgian eid card etc...)
At the the time, those cards worked through a magstripe (black border on the back of card) wich was read by a magnetic card reader. Today they come with a chip on it. Most chips are following the ISO7816 standard, wich makes it easier reading them out.
How does a smartcard basically work?
To keep it simple, assume that the chip on the card, is a mini computer with an operating system. It has a kind of a processor, Rom and Ram memory etc... Further it has its own file structure, comparable with the directories we know from pc's (e.g. c:\mymap\mysubmap\myfile.txt), and its own applications! So you can read out the files, or you can use an application on the chip. The files can contain data such as name, birthdate etc... The applications can be used for example for checking a PIN, calculating a hash etc...
Lets start : What do we need?
So, what we are going to do is just reading out data, nothing more or less, because even this is not the simpliest thing to do or to understand. In our example we will read the BELGIAN EID card and will retreive the name, surname etc...
Every chip card according to the ISO7816 standard can be accessed in the same way, but nevertheless, you have to read the specific manuals about the type of card you want to use. Every type of card has its own directory and file structure, own applications etc...and all this is described is manuals that comes with the chips. So, the manuals we need for the EID can be found here:
eid card specifications (very important)
eid chip structure explanation
i will refer to some pages later on in this blog.
Now, since java 6 , there is a new library, called javax.smartcardio. It allows you to access smartcards on a more simple way than ever before. So be sure java 6 jdk is installed. Also, be sure that you have a decent card reader installed on your development system. And of course...a belgian eid card!
I assume you know how to work with eclipse/websphere...
Start coding!
Creating our class file with the necessary imports is very simple...
package com.smartcard.readers; import java.util.Arrays; import java.util.List; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardNotPresentException; import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import javax.smartcardio.TerminalFactory; public class EidReader { }
Now our class is created, so we can start coding the usefull things!
We need to create a command, that tells the smartcard to select the identity file on the chip. Such a command will later on be executed trough an APDU. Here the manual comes in, and there we have to look how we can build a fileselect command. It looks like this :
static byte[] IDENTITY_FILE_AID = new byte[] { (byte) 0x3F,// MASTER FILE, Head directory MF "3f00" (byte) 0x00, (byte) 0xDF,// Dedicated File, subdirectory identity DF(ID) "DF01" (byte) 0x01, (byte) 0x40,// Elementary File, the identity file itself EF(ID#RN) "4031" (byte) 0x31 };
Once the file is selected and the file is read out, we have to select the fields we want. Those fields are encoded as follows:
static byte FIRST_NAME_TAG = (byte) 0x08; static byte LAST_NAME_TAG = (byte) 0x07; static byte NATIONAL_NUMBER_TAG = (byte) 0x06; static byte BIRTH_DATE_TAG = (byte) 0x0C;
So, now that we know the basic commands, we can start coding.
Lets create a main function in our program, where we can put our card-read-out code.
The first thing we will do is, locating all the cardterminals, and pick a card. To keep it simple, we will pick the first reader and card, and assume that its the good one to connect. (more than 1 card can be connected...). A channel will be set up to communicate with the card. This channel will be used to send the messages.
TerminalFactory factory = TerminalFactory.getDefault(); List<cardterminal> terminals = factory.terminals().list(); // get the first terminal CardTerminal terminal = terminals.get(0); // establish a connection with the card Card card = terminal.connect("*"); CardChannel channel = card.getBasicChannel();
So, we are ready to send our first messages to the card.
We allready know the IDENTITY_FILE_AID, but we need to tell the system that we want to select a file, more specific, the IDENTITY_FILE_AID.
So we will build up the message and send it!!
// SELECT FILE COMMAND // See BEID cardpecs v2.0 page 21 CommandAPDU selectFileApdu = new CommandAPDU( 0x00, //CLA 0xA4, //INS 0x08, //P1 0x0C, //P2 IDENTITY_FILE_AID); ResponseAPDU r = channel.transmit(selectFileApdu);
Now that the identity file is selected on the card, we can start reading it out. We will read it out in pieces until there is no data anymore.
int offset = 0; byte[] file = new byte[4096]; byte[] data; do { CommandAPDU readBinaryApdu = new CommandAPDU( 0x00, //CLA 0xB0, //Read binary command offset >> 8, //OFF_H higher byte of the offset (bit 8 = 0) offset & 0xFF, //OFF_L lower byte of the offset 0xFF); //empty ResponseAPDU responseApdu = channel.transmit(readBinaryApdu); data = responseApdu.getData(); System.arraycopy(data, 0, file, offset, data.length); offset += data.length; } while (0xFF == data.length); card.disconnect(false);
Now, all the data is stored in the file byte array. It contains our data like first name, last name etc...BUT...as an extra, it is stored in the TLV (tag/type length value) format.
If you google for this you will find out how it is read.
Here, as example, we will read out our parameters...
// locate the first name field within the identity file int idx = 0; byte length = 0; while (idx < file.length) { byte tag = file[idx]; idx++; length = file[idx]; idx++; if (LAST_NAME_TAG == tag) { String name = new String(Arrays.copyOfRange(file, idx, idx + length)); System.out.println("name: " + name); } if (NATIONAL_NUMBER_TAG == tag) { String number = new String(Arrays.copyOfRange(file, idx, idx + length)); System.out.println("national number: " + number); } if (BIRTH_DATE_TAG == tag) { String number = new String(Arrays.copyOfRange(file, idx, idx + length)); System.out.println("birth date: " + number); } if (FIRST_NAME_TAG == tag) { String firstName = new String(Arrays.copyOfRange(file, idx, idx + length)); System.out.println("first name: " + firstName); } idx += length; }