Table of Content
-
How Can I Add Libchipcard Support to My Own Applications ?
-
How Can I Use Libchipcard ?
-
Data Organization On Chip Cards
-
Sending Your Own Command to the Chipcard
1. How Can I Add Libchipcard Support to My Own Applications ?
Well, the easiest way is to use the automake/autoconf family of tools. To add libchipcard into your project
your ./configure.in script should include these lines:
AC_LIBCHIPCARD(0.8.0)
if test -n "$libchipcard_dir"; then
all_libraries="$all_libraries $libchipcard_libs"
all_includes="$all_includes $libchipcard_includes"
AC_DEFINE(HAS_LIBCHIPCARD)
define_has_libchipcard="#define HAS_LIBCHIPCARD"
fi
AC_SUBST(define_has_libchipcard)
|
Table: Lines to be included in configure.in
You'll need to add a line saying #undef HAS_LIBCHIPCARD to your acconfig.h file, otherwise
aclocal/autoconf/automake will complain about HAS_LIBCHIPCARD not being covered
by config.h.
Your Makefile.am files could then look like this (taken from tutorial subdir):
noinst_PROGRAMS=tutorial1
INCLUDES=@all_includes@
tutorial1_SOURCES = tutorial1.cpp
tutorial1_LDADD = @all_libraries@
|
Table: Example .Makefile.am
If you want optional support for libchipcard then you can use this in your *.cpp/*.c/*.h files:
#ifdef HAVE_CONFIG_H
# include <config.h> // need this to define HAS_LIBCHIPCARD
#endif
#ifdef HAS_LIBCHIPCARD
# include <chipcard.h>
// insert code that uses libchipcard
#endif
|
Table: Example test.cpp
With this in your program files you can use LibChipCard if available or exclude chipcard support if
LibChipCard is missing. This is the way OpenHBCI handles it.
2. How Can I Use Libchipcard ?
Below is a simple graph which shows the most interesting classes.
From the developers point of view the class CTCard (and its children) is the most important
one. This in fact is the only class you have to deal with. Therfore we'll have a look at this
one and will later discuss the inheriting classes.
Introduction Into Communication With Chipcards
Generally, a program sends some specific commands to the chipcard via the reader (terminal), this
is the so called APDU. It contains at least:
Name
|
Description
|
CLA
|
this is the command class byte (in most cases this is 0)
|
INS
|
the instruction byte, specifiying what the card is supposed to do
|
P1
|
first parameter byte, depends on the instruction the card has to perform
|
P2
|
second parameter byte, depends on the instruction, too
|
This APDU (plus some other control and data bytes) is all packed into a struct called CTCommand
(please refer to the API documentation for it's content) and send to the card via the reader.
After the cards response Libchipcard returns the answer and the result code in the very same struct.
As you can see CTCommand is the central object in a dialog with a chip card.
CTCard
Now, to communicate with the card, you need to setup the described CTCommand struct and feed it
to CTCard's method execCommand. This method actually sends the APDU from this struct to
the card and retrieves its answer.
Well, before you can send anything to the card you must prepare it by calling CTCard::openCard().
This method loads the appropriate driver library (CTAPI), initializes the card reader (if it not already
is) and connects the card to the reader. If this succeeds you can start sending commands.
After you have finished working with the card you may want to release it, thus allowing the user to remove
the card safely from the reader. This can be achieved by calling CTCard::closeCard().
There are some other interesting methods in this class, but these are the most important ones.
Please refer to the api documentation (downloadable in a separate package from the project page, see this site's
download section).
CTMemoryCard
Since there are two basic types of chipcards - memory cards and processor cards - libchipcard offers one
class for each of them.
This class provides some basic methods which simplify accessing memory cards (like German health insurance
cards). These methods allow you to
-
select files on the card
-
read data from the card
-
write data onto the card
The implementation is simple: These methods all create CTCommand objects, fill them with appropriate
data for the corresponding instruction, send this command to the card via execCommand() and return
the result in a CTError object. If you want to check for an error simply call the method isOk()
of this CTError object.
You should look at the source of this class to see how simple it is to implement new commands.
CTProcessorCard
This is the other basic class, it works on processor cards (like EC cards and HBCI cards).
You can:
-
select files on the card (there are more variants in selection than with memory cards, again, see apidoc)
-
read data from files on the card
-
write data to files on the card
Support For Special Cards
Generally, libchipcard offers basic support for chip cards.
However, there are two kinds of special chip cards this library supports directly:
-
German health insurance cards (Krankenversichertenkarte). There is even a program that dumps
the content of such a card, called qkvk.
-
HBCI cards (home banking cards, the project
openHBCI uses libchipcard to access those cards).
-
Memory Cards they are mentioned here again, because LibChipCard provides a file system for storage
of files on an ordinary memory card.
This support is indicated by the existence of the classes
-
CTKVKCard (for health insurance cards)
-
HBCICard (guess what)
-
CTCardMedium provides the file system. You should have a look at CTFile and CTDirectory,
too.
-
RSACard (for RSA HBCI cards)
-
CTGeldKarte (for German Geldkarten used to digitally store small amounts of money)
3. Data Organization On Chip Cards
A chip card normally has the following structure:
Master File
The master file is the root of the data on the chip card (referred to as "MF"
throughout the api documentation). As with The Highlander there can only be
ONE master file.
This master file may have some "dedicated files"("DF") and "element files"
("EF").
Dedicated File
This is best described as a kind of directoy, which includes element files.
A dedicated file itself does not contain data.
You can select such a data file using the various "selectXX" methods of the
card classes.
Element File
An element file now finally contains the data.
You can select such an element file using the various "selectXX" methods of
the card classes.
To make it more complicated some cards (mostly memory cards, like German
health insureance cards) do not have data files or element files at all.
They store their data directly in the master file. And these cards do NOT
allow calling the "selectFile" method with other values than "0x3f00", which
specifies the master file.
But that looks more complicated than it really is, because you rarely try to
read cards you don't have the slightest piece of information for.
In most cases the documentation for the card you want to read tells you if
there are some data/element files to select.
For example the German health insureance card does NOT have those files
and therefore you can read the data directly from the master file after and even without
selecting it (with selectFile(0x3f00)).
4. Sending Your Own Command to the Chipcard
Now that you know how data is stored on a chip card and how commands are send to the card we can play
around with it. Let's create an example that selects a file on a memory card. To make this example be of use
in any case we simply select the Master File which is accessable on nearly all memory cards (remember, the
simplest memory card is the German medical card, so you can use it for this example).
Well, of course, selecting a file can be done more easily with the already existing methods of CTProcessorCard and
CTMemoryCard, but hey, it's just an example ;-)
This code assumes to be part of a class which inherits CTCard, so the method execCommand() is
available. Another assumption is that the card has already been openend via openCard().
We'll see the code first and discuss later.
CTCommand cmd;
|
// we need this to send a command to the card
|
CTError err;
|
// this object will receive the error code our command returns
|
cmd.setCla(0x00);
|
// class the command belongs to
|
cmd.setIns(0xa4);
|
// the code of the command Select File
|
cmd.setP1(0x00);
|
|
cmd.setP2(0x0);
|
|
cmd.setLr(255);
|
// expected length of the answer from card
|
cmd.setData((char) 0x3f);
|
// file id (High byte first)
|
cmd.addData((char) 0xff);
|
// file id (Low byte)
|
err=execCommand(cmd);
|
if (err.isOk()) return true;
|
// return if there was no error
|
printf("Error was: %s\n",err.errorString().c_str());
|
// show the error code if there was an error
|
|
Ok, let's now discuss the code line by line.
-
CTCommand cmd;
CTError err;
We need an object of the class CTCommand to send to the card. It will be filled with data in the following
lines. The other object err will receive the result code returned by the method which really executes
our command (CTCard::execCommand()).
-
cmd.setCla(0x00);
Commands to the chip card are grouped into command classes.
Since the command to select a file on a card is a standard chip card command defined in ISO 7816-4
the command class is 0x00. Most of the other commands you could possibly send to a chip card fall into this
class.
-
cmd.setIns(0xa4);
The command we want to send is Select File. It's ISO code is 0xa4, so this is the instruction code
to store here.
-
cmd.setP1(0x00);
cmd.setP2(0x00);
As you read above a Chip Card Command has two fixed arguments, called p1 and p2. For some
commands these have no special meaning. This is the case with our command. Well, in fact, these parameters
do have a meaning for Select File, but for now we simply ignore that.
-
cmd.setLr(255);
This member of CTCommand tells the chip card how many bytes of data we want it to return upon execution
of our command. You might ask what data could possibly be returned upon selecting a file ?
Well, some cards - especially processor cards - return a data block called File Control Information.
We don't need this information now, but for the curious among you: have a look into the class CTTLV_FCI.
When looking at the members of that class you will see what this block contains.
-
cmd.setData((char) 0x3f);
cmd.addData((char) 0xff);
The real arguments to most interesting commands are transmitted in the data area of the APDU.
The argument to Select File is the identifier of the file we want to select. This id is a 16-bit
number. To store 16 bit we need two bytes, so the data area contains those two bytes stored in
Big Endian (which simply means: most significant byte first, the other mode called Little Endian
is used on intels ix86 architecture. Anyway, the chip cards want the big guy).
-
err=execCommand(cmd);
Now that we have setup the whole command object we can give it to the method CTCard::execCommand()
which will send it to the chip card and return it's answer in the very same object.
-
if (err.isOk()) return true;
Well, not all days are good days so our command - even if it is that good prepared like ours - can fail.
And that's where CTError comes around. It serves as an extended bool variable. If you simply
want to know if there was an error simply call CTError::isOk().
-
printf("Error was: %s\n",err.errorString().c_str());
This shows an informative error message. You might want to look at the API cocumentation of the class
CTError because this class has some usefull members and methods.
What I did not show in this example was that the command may have returned some data. If you are interested
in that data you can find it in the member CTCommand::data (in our case this is cmd.data).
Well, isn't it easy to send a command to the card ? This is the way other classes implement new commands.
As you already know the class CTCard only contains very basic commands which are encapsulated int some methods,
like CTCard::openCard() and so on.
Inheriting classes simply define new methods which themselves send commands to the chip card by exactly the way
described above.
So the command you see above is derived from a fragment of the method CTMemoryCard::selectFile().
In fact you should never call the method CTCard::execCommand() directly, better write a class which
inherits CTCard and place the command execution there, thus encapsulating access to the chip card.
If you have written a class that includes commands usefull for a special card type (like cell phone cards) I
would absolutely appreciate if you send them to me.
I would gladly incorporate these classes into future versions of libchipcard after testing them and name you as
the author of that class.