Konstantin Filtschew WebLog

Der tägliche IT-Wahnsinn

Smart Cards durch die in Java eingebaute Java Smartcard I/O javax.smartcardio.* ansprechen

Zuerst mache ich eine kleine Einführung in das Thema Java und Smartcads und beschreibe danach wichtige Punkte für Linux und Windows.

Java hat seit Version 1.5 (5.0) eine Unterstützung für Smartcards bereits eingebaut. Es nutzt dafür die eigene Provider Technik und greift über PC/SC auf die Smartcard zu. Das bedeutet es sind keine weiteren Module für Java notwendig (früher JPCSC für Linux) und die Nutzung erfolgt unabhängig vom Betriebssystem. Zumindest kann ich das für Windows XP/Vista und Linux bestätigen. FreeBSD und Sun Solaris sollten genau so gut funktionieren. Weitere Informationen zu Betriebssystemen in den nächsten Absätzen.

Unter Windows ist die Verwendung denkbar einfach. Für die Nutzung ist ein installierter Treiber für das Lesegerät (Terminal) und natürlich Java notwendig. Windows bringt eine eigene PC/SC Implementierung mit, die Java über den eingebauten Provider nutzt. Dadurch ist die Installation und Einrichtung sehr einfach.

Für Linux ist die Installation fast genau so einfach. Durch die verschiedenen pcsclite-Versionen (PC/SC Implementierung unter Linux) sind aber die Header-Datei (.h) für die Bibliothek notwendig, damit Java die PC/SC Bibliothek richtig ansprechen kann. Dafür reicht meistens die Installation der folgenden Pakete:

  • libpcsclite1
  • pcscd
  • libccid
  • libpcsclite-dev  (WICHTIG: Hier sind die notwendigen Header-Dateien)

Fehlt das Paket libpcsclite-dev bzw. die Header Dateien, so kann Java die PC/SC Bibliothek nicht nutzen und findet deswegen kein Lesegerät (Terminal).

Für Debian/Ubuntu und weitere Debian-Derivate ist die Installation denkbar einfach:

CODE:
  1. sudo aptitude install libpcsclite1 pcscd libccid libpcsclite-dev

Die Nutzung des JPCSC Treibers, welcher veraltet ist, habe ich bereits früher hier beschreiben.

Um die Verwendung der Java Smartcard I/O zu testen, habe ich ein paar kleine Testprogramme geschrieben, die ich hier veröffentliche. Sie sind auf Englisch kommentiert, so dass keine weitere Erklärungen erfolgen. Ich denke die paar Worte Englisch wird wohl jeder verstehen können. Sie sind extra kurz und einfach gehalten.

CODE:
  1. import java.util.List;
  2. import javax.smartcardio.*;
  3.  
  4. /*
  5. * Look in rt.jar for "javax.smartcardio" to get further information
  6. * Defined in: JSR 268
  7. */
  8. public class ListSmartcardReaders {
  9.  
  10.     public static int listCounted() {
  11.        
  12.         /* we use the default TerminalFactory */
  13.         TerminalFactory factory = TerminalFactory.getDefault();
  14.        
  15.         try {
  16.             /* We can have multiple terminals on one System, so we get a list */
  17.             List<cardterminal> terminals = factory.terminals().list();
  18.            
  19.             for (CardTerminal terminal : terminals) {
  20.                 System.out.println("Card_Terminal_Name: "+ terminal.getName());
  21.                 System.out.println("Card_in_Terminal_present: "+terminal.isCardPresent());
  22.                 System.out.println("--------------------------------------------");
  23.             }
  24.             return terminals.size();
  25.            
  26.         } catch (CardException e) {
  27.             e.printStackTrace();
  28.         }
  29.         return 0;
  30.     }
  31.    
  32.     public static void main(String[] args) {
  33.         listCounted();
  34.     }
  35. }

CODE:
  1. import java.util.List;
  2. import javax.smartcardio.*;
  3.  
  4. /*
  5. * Look in rt.jar for "javax.smartcardio" to get further information
  6. * Defined in: JSR 268
  7. */
  8. public class ConnectReader {
  9.  
  10.     /**
  11.      *
  12.      * @param index is the array index of the terminal list (0..x)
  13.      * @throws CardException if there are problems accessing the smartcard
  14.      */
  15.     public static void connectCard(int index) throws CardException {
  16.        
  17.         /* Is a Reader connected we can access? */
  18.         if (TerminalFactory.getDefault().terminals().list().size() == 0) {
  19.             System.err.println("No reader present");
  20.             return;
  21.         }
  22.        
  23.         /* Terminal we are working on */
  24.          CardTerminal terminal = TerminalFactory.getDefault().terminals().list().get(index);
  25.        
  26.         /* Is a card present? */
  27.         if (!terminal.isCardPresent()) {
  28.             System.err.println("No Card present!");
  29.             return;
  30.         }
  31.        
  32.         /* Here you have to choose "T=0","T=1", "T=2", check documentation of your smart card */
  33.         //Mostly it's "T=1", for older cards its "T=0"
  34.         Card card = terminal.connect("T=1");
  35.        
  36.         System.out.println("Card_Info: "+card.toString());
  37.         System.out.println("Card Protocol: "+ card.getProtocol());
  38.        
  39.         //Reset the card for use
  40.         ATR atr = card.getATR();
  41.  
  42.         System.out.println("ATR: " + atr.getBytes());
  43.         System.out.println("ATR historical bytes: "+ atr.getHistoricalBytes());
  44.        
  45.         /* Get the basic channel. This one can't be closed */
  46.         CardChannel channel = card.getBasicChannel();
  47.        
  48.         /* Try to send a command. This one won't work! */
  49.         byte[] command = { 0, 0, 0, 0};
  50.        
  51.         CommandAPDU someApdu = new CommandAPDU(command);
  52.        
  53.         ResponseAPDU r = channel.transmit(someApdu);
  54.         /* Response encoded in bytes */
  55.         byte[] response = r.getBytes();
  56.        
  57.         System.out.println("response: "+ response);
  58.            
  59.         card.disconnect(false);
  60.     }
  61.    
  62.    
  63.     public static void main(String[] args) {
  64.         try {
  65.             //First Terminal = 0
  66.             connectCard(0);
  67.         } catch (Exception e) {
  68.             e.printStackTrace();
  69.         }
  70.     }
  71. }

Für weitere Information über APDUs und die Nutzung der Smartcards empfehle ich das Lesen des Buches Handbuch der Chipkarten oder direkt die dazugehörigen ISO-Normen ISO-7816 und im Speziellen die ISO-Normen ISO7816-4, ISO7816-8 und ISO7816-9.

Auf die Frage "Kommt man ohne das Buch bzw. die Normen aus?", muss ich leider mit "definitiv nein!" beantworten, da die Informationen sehr komplex sind und schwer zu verstehen. Im Internet ist bis Heute fast gar nichts vorhanden.
Ich rate aber nach Diplom-, Master- und Bachelorarbeiten zu dem Thema zu suchen. Da gibt es bestimmt Auszüge aus den Normen, um wenigstens die Zusammehänge und die Anfänge zu verstehen.

17 Reaktionen zu “Smart Cards durch die in Java eingebaute Java Smartcard I/O javax.smartcardio.* ansprechen”

  1. Dave

    Hi Konstantin,
    ich wollte eine Applikation schreiben die Daten von einer SmartCard liest und diese verarbeitet.
    Leider kann ich meinen Reader nicht ansprechen, sprich “no Terminals present”. Ich benutze einen Chpdrive extern 320 von TOWITOKO. Treiber sind natürlich installiert. Weisst du was man noch machen konnte? Die Reader Software erkennt den Reader und die Kartendaten werden angezeigt, nachdem ich eine Karte reinsteckt habe.

    Ich bin für jede Hilfe dankbar.
    Grüße
    Dave

  2. Dave

    Ich benutze WinXP und der Reader ist im Gerätemanager zu sehen.

    Danke im Voraus !!!
    Grüße Dave

  3. Rafael

    Hi Konstantin,
    ich habe dein Code ( ListSmartcardReaders ) getestet und was mir einfällt , ist das Eclipse anfängt zu meckern (Errors), weil es Access Restrictions in der RunTime library rt.jar gibt. Man kann es lösen in dem man in den Einstellungen die deprecated API auf Warning umstellt. Weisst du evtl. was es für restrictions sind ???

    Grüße Rafael

  4. Konstantin Filtschew

    Hallo Rafael,

    das habe ich bereits früher in einem Beitrag geschrieben:
    http://konstantin.filtschew.de/blog/2009/07/25/unter-eclipse-ueber-java-auf-zugriffsbeschraenkte-not-accessible-pakete-am-beispiel-von-javax-smartcardio-im-java-projekt-zugreifen/

    Ich habe mich nicht weiter mit Restrictions beschäftigt. Das Beispiel entspricht aber so dem Beispiel von den Javadocs. Das bedeutet das Beispiel von SUN hat auch das Problem.

    Wenn du heraus findest, warum die Meldung kommt, dann schreib es mir bitte. Wenn ich es finde, dann werde ich es noch ergänzen oder einen neuen Beitrag dazu schreiben.

    Danke, Konstantin

  5. Konstantin Filtschew

    Hallo Dave,

    der Kartenleser muss PC/SC unterstützen, da SUN bis jetzt nur einen PC/SC Provider für die Java Smartcard I/O geschrieben hat. Meines Wissens nach gibt es im Moment auch keine weiteren Provider.

    Schau bitte selber bei Google oder beim Hersteller, ob dein Kartenleser bzw. der Treiber PC/SC unterstützt. Vielleicht fehlt auch noch was oder es gibt noch ein extra PC/SC Modul.

    Wenn du noch Fragen hast, dann melde dich.

    Gruß, Konstantin

  6. Dave

    Hi Konstantin,
    Vielen Dank fpr die Antwort, mein Reader unterstützt PC/SC und das ist ja im Windows ia integriert. Ich habe auch im dem Chipdrive Diagnostic Tool die Funktionalität überprüft. Alles sieht in ordnung aus.
    hier die Screenshots des diagnose Tools:
    - der ausführliche Test : http://www.imgbox.de/?img=d50146x201.jpg

    - die Einstellungen : http://www.imgbox.de/?img=d25899r201.jpg

    ausführung des Java Codes verursacht folgende Exceptions:

    javax.smartcardio.CardException: list() failed
    at sun.security.smartcardio.PCSCTerminals.list(Unknown Source)
    at javax.smartcardio.CardTerminals.list(Unknown Source)
    at smartcardtest.ListSmartcardReaders.listCounted(ListSmartcardReaders.java:21)
    at smartcardtest.ListSmartcardReaders.main(ListSmartcardReaders.java:37)
    Caused by: sun.security.smartcardio.PCSCException: SCARD_E_NO_READERS_AVAILABLE
    at sun.security.smartcardio.PCSC.SCardListReaders(Native Method)
    … 4 more

    Die treiberstruktur sieht laut Hersteller folgendermassen aus:
    http://www.imgbox.de/?img=b12119l201.jpg

    Ich bin für weitere Ideen und Tipps Dankbar
    Grüße
    Dave

  7. Konstantin Filtschew

    Hallo Dave,

    deaktiviere mal diesen PC/SC Resource Manager als Test.

    Ich glaube nicht, dass es an Java liegt. Hast du einen aktuellen JDK (1.6.X) bei dir installiert?

    Probier bitte den Kartenlesern an einem anderen PC, damit ein Problem am PC ausgeschlossen wird. Das Programm kannst du ja ohne Eclipse starten.

    Wenn es am anderen PC funktioniert, dann installiere JAVA und Treiber für den Kartenleser neu.

    Schau mal, ob es ein Firmware update für deinen Kartenleser gibt.

    Ansonsten finde ich auch selber gar nichts zu dem Thema in Google. Deswegen kann man fast davon ausgehen, dass es an dem einen PC liegt, an dem du das probierst.

    Melde dich, falls du noch eine Frage hast. Schreib vielleicht auch den Hersteller an. Vielleicht brauchst du ein Firmware Update oder das Problem ist dem Support bekannt.

    Gruß, Konstantin

  8. Dave

    Hi Konstantin,

    ich habe die Einstellung vorgenommen mit dem Ergebnis, dass es keine Excepton mehr gegeben hat dennoch ist der Reader nicht erkannt worden,

    factory.terminals().list().size() liefert 0 obwohl man ihn in dem Gerätemanager sieht. Ich hatte jdk 1.6.0_13-b03 installiert gehabt, nun habe ich auch mit der 1.6.0_17-b01 compiliert und ausgeführt . Es hat sich nichts geändert.

    Da ich nur ein Notebook zu Hause habe, werde ich es bei meinem Brüder versuchen müssen. Ich hoffe dass ich noch diese Woche weiter berichten kann.

    Auf jeden Fall funktioniert der Reader unter WinXP mit der mitgelieferten Software unter WIn7 bekam ich einen Bluescreen nachdem ich den Treiber installiert habe. Der Hersteller hat noch keine Treiber für Win7 und soweit ich mtbekommen gibt es auch keinen für Vista erst ab einer neueren Reader-Version.

    Falls ich es zum Laufen bekomme, werde ich hier berichten.

    Vielen Dank soweit

    Grüße Dave

  9. Dave

    Hi Konstantin,

    ich habe es auf dem Rechner von meinem Brüder ausprobiert, leider ohne Erfolg.

    factory.terminals().list().size() liefert 0

    ich werde es mit einem anderen Reader versuchen müssen. :/

    Danke für bisherige Hilfe
    Grüße
    Dave

  10. Dave

    Hi Konstantin,

    ich habe bis jetzt noch keinen anderen reader besorgt, und wollte es dennoch mit dem alten versuchen. Es gibt ja noch die CTAPI – bist du damit evtl. konfrontiert worden ?

  11. Konstantin Filtschew

    Hallo Dave,

    nein mit der CTAPI habe ich bis jetzt nichts gemacht. Sie ist nur im deutschsprachigem Raum verbreitet und gilt als “veraltet”.
    PC/SC ist im Moment der Standard für Smartcard Communication und wird es vermutlich noch eine Zeit lang bleiben.

    Siemens stellt zwar sehr gerne für ihre Smart Cards eigene Bibliotheken und APIs zur Verfügung, damit die Leute mit APDUs nicht an deren Karten rumspielen, aber das ist Firmenpolitik und ich finde es kontraproduktiv. Der Kram läuft natürlich nur unter Windows.

    Ich weiß im Moment nicht so wirklich, wie ich dir helfen kann. Hast du beim Hersteller angefragt? Eigentlich ist der Hersteller für so Probleme zuständig. Versuche einfach mal dein Glück.

    Falls du einen neuen Kartenleser suchst, so kann ich dir Omnikey empfehlen, weil die Entwicklung und der Support in Deutschland sitzt. Sie sind sehr kompetent und reagieren auch bei Anfragen bezüglich Linux und neuen Karten.

    Ansonsten sind die Lesegeräte von SCM Electronics auch sehr gut. Den Support von SCM habe ich nicht getestet, da der Reader so ohne Probleme mit allen Karten funktioniert hat.

    Falls du dir ein neues Lesegerät zulegst, nimm dir am besten einen mit RFID Schnittstelle. Bald haben wir alle ganz viele funkende Karten neben dem elektronischen Reisepass.

    Folgende Lesegeräte kann ich empfehlen (bei E-Bay teils sehr günstig):
    SCM Microelectronics SDI010
    Omnikey Cardman 5321

    Gruß, Konstantin

  12. Dave

    Hi Konstantin,

    bei mir hat sich momentan nichts getan. Ich habe ledigliech erfahren das ich für mein Vorhaben gezwungen bin die CTAPI zu benutzen – denn wer Anwendungen entwickelt, die die Daten von den Krankenkarten auslesen, muss die CTAPI benutzen denn nur diese ist dazu zugelassen. Ich werde also weiter recherschieren müssen, um den Wrapper zu finden und zu integrieren.

    Danke für die Tipps
    werde mir das Gerät anschauen müssen.

    Grüße
    Dave

  13. Peter Huebner

    KVK-Karten? Das sind Memory-Karten, die vom PCSC-Standard nicht unterstützt werden. PCSC kann meines Wissens nur was mit Prozessorkarten anfangen. es gibt in der PCSC-Implementierung unter Windows zwar auch einen (sehr dünnen) Layer, um Memory_Karten mittels (Peudo-APDUs) anzusprechen, meines Wissens nach funzt das aber selten bis gar nicht.
    Unter PCSC gibt es kein ReadMemory oder WriteMemory, PCSC funktioniert mit APDUs (T=0 oder T=1), aber Speicherkarten (wie die KVK) können APDUs nicht interpretieren (haben ja keinen Prozessor).
    Gruss Peter

  14. Chris

    Hallo,

    ListSmartcardReaders funktioniert bei mir,
    Card_Terminal_Name: OMNIKEY CardMan 5×21 0
    Card_in_Terminal_present: true

    Auch die andere Anwendung geht:
    Card_Info: PC/SC card in OMNIKEY CardMan 5×21 0, protocol T=0, state OK
    Card Protocol: T=0
    ATR: [B@5afd29
    ATR historical bytes: [B@1a2961b
    response: [B@15dfd77

    Musste nur das Protokoll für die Karte ändern auf T=0.
    Wollte mich hiermit nur mal bedanken, da ich es für mein Studium brauche !

    Kartenleser von OMNIKEY
    Karte : von wrankl.de (lag dem buch als Testeksemplar bei)
    mfg
    Chris

  15. Oleg

    Hi Konstantin,
    ich arbeite gerade an der Kommunikation mit einer Karte über PC/SC und habe ein Problem: lt. Spezifikation der Karte muss APDU nach ISO 7816-4 aufgebaut sein, mit dem Vermerk, dass LE Byte immer vorhanden sein muss, auch wenn es 0×00 ist. Wenn ich APDU aufbaue z.B.: 90 5A 00 00 03 01 00 00 /*LE*/ 00 und diese über CardChannel – Funktion transmit sende, dann wird dieser LE-Byte abgeschnitten und ich bekomme zurück 91 + „invalid telegram“.

    Ähnliches Verhalten habe ich auch bei jpcsc festgetstellt. LE Byte wird immer abgeschnitten.

    Zusätzlich, wenn ich die Daten mit der Funktion transmit(ByteBuffer arg0, ByteBuffer arg1) sende, also 5A 01 00 00, dann wird die Telegramm geändert und stattdessen 18 01 00 00 geschickt. Als ob es ein Überlauf bei 0×40 statt findet. Ist es ein Bug oder habe ich was nicht beachtet?

    Ich werde froh sein, wenn du mir paar Tipps geben kannst. Danke.

    Gruß, Oleg

  16. Konstantin Filtschew

    Hallo Oleg,

    normalerweise wird nichts abgeschnitten oder verändert. Wie kommst du drauf, dass es verändert wird? Hast du einen Sniffer an der USB/Com Schnittstelle laufen?

    Du hast bei deinem Befehl das Class Byte auf 90 gesetzt. Das bedeutet, du verwendest propriätere Befehle, die nicht in der ISO 7816-4 spezifiziert sind. Da kann ich dir schwer helfen, da ich nicht weiß, was die Befehle bedeuten oder was du vor hast.

    Einen Bug in dem Bereich kann ich mir schwer vorstellen. Verwendest du Java Smartcard I/O oder? Es kann noch sein, dass dein Treiber vom Lesegerät ein Problem hat. Das kann ich aber ohne Informationen nicht einschätzen.

    Melde dich, wenn du noch Fragen hast.

    Gruß, Konstantin

  17. Oleg

    Hi,

    T=0 Protokoll unterstützt LE Byte nicht, das wird erklären wieso LE Byte nicht mitgeschickt wurde. Es muss auf jedenfall bei neuen Karten, wenn LE mitgeschickt werden muss, auf T=1 gehen.

    Das mit dem 0x5A to 0×18 vermute ich, dass es an dem “speziellen” Befehl liegt. Wenn ich es in den Wrapper Befehl packe (ist von der Karte vorgeschrieben), funktioniert alles wunderbar, auch wenn den Befehl direkt über “PCSC Test Applikation” sende.

    Auf jedenfall hat Konstantin recht: Es ist kein Bug und es wird nix verändert. Die Definitionen sollen einhalten werden :) , dann klappt vielleicht alles.

    Gruß, Oleg

Einen Kommentar schreiben

Copyright © 2010 by: Konstantin Filtschew WebLog • Template by: BlogPimp Lizenz: Creative Commons BY-NC-SA.